How to Make Databinding Type Safe and Support Refactoring

How do I make WPF data bindings refactor safe?

You could use a lambda expression to express the property name, rather than using the name directly :

    protected static string GetPropertyName<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
{
if (expression.NodeType == ExpressionType.Lambda && expression.Body.NodeType == ExpressionType.MemberAccess)
{
PropertyInfo prop = (expression.Body as MemberExpression).Member as PropertyInfo;
if (prop != null)
{
return prop.Name;
}
}
throw new ArgumentException("expression", "Not a property expression");
}

You would use it like that :

...
DisplayMember = new Binding(GetPropertyName((MyDataObject o) => o.FooProperty))
...

OK, it's a bit verbose... If you want something shorter, you could also create a helper method :

public Binding CreateBinding<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
{
return new Binding(GetPropertyName(expression))
}

...
DisplayMember = CreateBinding((MyDataObject o) => o.FooProperty)
...

That way, the refactoring should work fine if you rename the property (except in the XAML of course...)

ListBox.DisplayMember = [String] Can I somehow workaround it to be other than string in .NET?

The only way i have found around this is to use extra properties on the objects so have a

string DisplayMember
{
get { return 'name'; }
}

and when you refactor your object you then only need to change the returned string to your new property name making it a change in one location rather than in a few.

It's not great but works better than hardcoding in the app!

HTH

OneSHOT

Adding nameof to C# through backdoors

Doing any pre-compilation code changes would be an extreme hassle. So I would avoid it.

I wouldn't worry too much about performance - most times when you need a property name as a string you'll end up doing reflection or IO anyway, so performance is going to be relatively slow anyway.

Here's what I suggest to use:

public static string ToPropertyName<T>(this Expression<Func<T>> @this)
{
var @return = string.Empty;
if (@this != null)
{
var memberExpression = @this.Body as MemberExpression;
if (memberExpression != null)
{
@return = memberExpression.Member.Name;
}
}
return @return;
}

Now you can do this:

Expression<Func<T>> px = x => x.Foo;
var pn = px.ToPropertyName(); // == "Foo"

Workaround for lack of 'nameof' operator in C# for type-safe databinding?

This code basically does that:

class Program
{
static void Main()
{
var propName = Nameof<SampleClass>.Property(e => e.Name);

Console.WriteLine(propName);
}
}

public class Nameof<T>
{
public static string Property<TProp>(Expression<Func<T, TProp>> expression)
{
var body = expression.Body as MemberExpression;
if(body == null)
throw new ArgumentException("'expression' should be a member expression");
return body.Member.Name;
}
}

(Of course it is 3.5 code...)

New to WPF and Databinding

Please check following links. Hope it helps.

http://msdn.microsoft.com/en-us/library/aa480226.aspx

http://www.codeproject.com/Articles/24192/Simple-Demo-of-Binding-to-a-Database-in-WPF-using

pyCharm: safe refactoring information for application on depending code

As far as I can tell this is not possible neither with a vanilla PyCharm nor with a plugin nor with a 3rd party tool.

  1. It is not mentioned in the official documentation
  2. There is no such plugin in the JetBrains Plugin Repositories
  3. If PyCharm writes refactoring information to it's internal logs, you could build this yourself (but would you really want to?)
  4. I am also not aware of any python specific refactorig tool that does that. You can check for yourself: there is another SO question for the most popular refactoring tools

But ...

I am sure there are reasons why your situation is like it is - there always are good reasons (and most of the time the terms 'historic and 'grown' turn up in explanations of these reasons) but I still feel obligated to point out what qarma already mentioned in his comment: the fact that you want to do something like replaying a refactoring on a different code base points towards a problem that should be solved in a different way.

Alternative 1: introduce an API

If you have different pieces of software that depend on each other on such a deep level, it might be a good idea to define an API that decouples the code bases from each others internals. With an API it is clear which parts have to be stable. If changes have to be done on the API level they must be communicated and coordinated with the involved teams.

Alternative 2: Make it what it actually is: one code base

If A1 for whatever reason is not possible I would conclude that you actually have one system distributed over different code bases and then those should be merged into one code base. Different teams can still work on the same code base (hopefully using a DVCS) but global refactorings can be done with tooling help and they reach all parts of the system.

Alternative 3: Make these refactorings in PyCharm over all involved code bases

Even if you can't merge them into one code base you could combine them easily in PyCharm by loading different projects into the same Window. I do this without problems with two git projects that have to be in different repositories but still share certain aspects. PyCharm handles commits to these repositories transparently: if you make changes in several repositories and commit them you write one commit message and the commits will be done to all repositories.

Is there a way of making C# binding work statically?

You can use Expressions to get compiler-checked bindings.
For example, in one of current projects we set up bindings like this:

DataBinder
.BindToObject(this)
.ObjectProperty(c => c.IsReadOnly)
.Control(nameTextBox, n => n.ReadOnly)
.Control(addressControl, n => n.ReadOnly)

Code supporting this style is separated into several classes:

public static class DataBinder
{
public static DataBinderBindingSourceContext<TDataSource> BindToObject<TDataSource>(TDataSource dataSource)
{
return new DataBinderBindingSourceContext<TDataSource>(dataSource);
}
}

public class DataBinderBindingSourceContext<TDataSource>
{
public readonly object DataSource;

public DataBinderBindingSourceContext(object dataSource)
{
DataSource = dataSource;
}

public DataBinderControlContext<TDataSource, TProperty> ObjectProperty<TProperty>(Expression<Func<TDataSource, TProperty>> property)
{
return new DataBinderControlContext<TDataSource, TProperty>(this, property);
}
}

public class DataBinderControlContext<TDataSource, TProperty>
{
readonly DataBinderBindingSourceContext<TDataSource> BindingSourceContext;
readonly string ObjectProperty;

public DataBinderControlContext
(
DataBinderBindingSourceContext<TDataSource> bindingSourceContext,
Expression<Func<TDataSource, TProperty>> objectProperty
)
{
BindingSourceContext = RequireArg.NotNull(bindingSourceContext);
ObjectProperty = ExpressionHelper.GetPropertyName(objectProperty);
}

public DataBinderControlContext<TDataSource, TProperty> Control<TControl>(TControl control, Expression<Func<TControl, TProperty>> property)
where TControl : Control
{
var controlPropertyName = ExpressionHelper.GetPropertyName(property);
control.DataBindings.Add(controlPropertyName, BindingSourceContext.DataSource, ObjectProperty, true);

return this;
}
}

public static class ExpressionHelper
{
public static string GetPropertyName<TResult>(Expression<Func<TResult>> property)
{
return GetMemberNames(((LambdaExpression)property).Body).Skip(1).Join(".");
}

public static string GetPropertyName<T, TResult>(Expression<Func<T, TResult>> property)
{
return GetMemberNames(((LambdaExpression)property).Body).Join(".");
}

static IEnumerable<string> GetMemberNames(Expression expression)
{
if (expression is ConstantExpression || expression is ParameterExpression)
yield break;

var memberExpression = (MemberExpression)expression;

foreach (var memberName in GetMemberNames(memberExpression.Expression))
yield return memberName;

yield return memberExpression.Member.Name;
}
}

public static class StringExtentions
{
public static string Join(this IEnumerable<string> values, string separator)
{
if (values == null)
return null;

return string.Join(separator, values.ToArray());
}
}


Related Topics



Leave a reply



Submit