问题
I am using PostSharp's [CompositionAspect] capability to dynamically inject an interface to a model object in my WPF application. However, it seems WPF cannot bind (display) properties corre ctly unless the relevant interface is explicitly implemented on the model object?
Using a custom CompositionAspect (ComposeObjectAspect) I am able to successfully expose the internal IMyInterface object's properties directly by runtime casting the ModelObject to the introduced interface;
// some interface definition, for sample completeness
public interface IMyInterface
{
public string MyProperty { get; set; }
}
// model object, which does NOT implement IMyInterface directly, though the interface is dynamically introduced by PostSharp
[ComposeObjectAspect(typeof(IMyInterface))]
public class ModelObject
{
private IMyInterface actualDataObject;
}
. . .
public class ViewModel
{
public ObservableCollection<IMyInterface> MyData { get; }
public void LoadData()
{
// concrete IMyInterface dtos are returned here, and put into a VM collection
IList<IMyInterface> myData =
(from IMyInterface o in SomeDataSource.LoadLocalDescriptors()
select new ModelObject(o) as IMyInterface).ToList();
this.MyData = new ObservableCollection<IMyInterface>(myData);
}
}
This all works fine, and if I inspect any object in the myData list, or cast it to the type IMyInterface (which as you can see form the class ModelObject definition above is not implemented directly), I can see the inner properties from actualDataObject directly. This all works great.
Now when I try to bind the ViewModel.Data collection to a WPF datagrid, I get a series of binding errors like;
System.Windows.Data Error: 40 : BindingExpression path error: 'SomeProperty' property not found on 'object' ''ModelObject' (HashCode=56810243)'. BindingExpression:Path=SomeProperty; DataItem='ModelObject' (HashCode=56810243); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
For reference, the relevant section of the View XAML looks like;
<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Width="Auto" Header="Some Property" Binding="{Binding SomeProperty, Mode=OneWay}" />
</DataGrid.Columns>
</DataGrid>
It is the grid columns which are appearing blank, though all data rows are visible (and empty).
If I inspect 'ModelObject', the property IMyInterface.SomeProperty is there.
If I directly apply IMyInterface to the ModelObject class (and implenment all members as simple relays to the composed actualDataObject instance) then the binding is fine, and SomeProperty displays without error.
What am I missing that is different between the PostSharp implementation of IMyInterface, and my own explicit version?
回答1:
I think the reason is that PostSharp introduces an explicit implementation, so actually no public property gets introduced. You should use an [IntroduceInterface] advice and [IntroduceMember] advices to introduce public properties.
回答2:
Possible solution:
<DataGrid DataContext="{Binding Data}" ItemsSource="{Binding}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Width="Auto" Header="Some Property" Binding="{Binding SomeProperty, Mode=OneWay}" />
</DataGrid.Columns>
</DataGrid>
or
<DataGridTextColumn Width="Auto" Header="Some Property" Binding="{Binding Path=actualDataObject.SomeProperty, Mode=OneWay}
回答3:
I managed to find a solution to this problem here: WPF databinding to interface and not actual object - casting possible?
Turns out the binding will work if it is defined explicitly against the interface type, instead of implicitly, presumably because ModelObject does not have a SomeProperty, but it does have IMyInterface.SomeProperty ?
So, this works;
<DataGridTextColumn Width="Auto" Header="Some Property"
Binding="{Binding Path=(mynamespace:IMyInterface.SomeProperty), Mode=OneWay}" />
Though, it does cause designer warnings, as noted in the referenced question.
来源:https://stackoverflow.com/questions/13636962/binding-wpf-controls-to-dynamically-introduced-interfaces-on-model-objects