In my WPF application, I have a lot of view models that, now, require a "property-value" like grid.
I'm trying to build a generic purpose view model that can be "injected" in existing ones so that I can use them with the new window with little no effort. To do this, I've written a simple interface with an observable collection:
public interface IPropertyGridVM
{
ObservableCollection<(string Prop, object Val)> PropValueList
{
get;
set;
}
}
And, in one of the View Models that needs the new property-value grid:
public class ExistingVM : ViewModelBase<Model>, IPropertyGridVM
{
public ObservableCollection<(string Prop, object Val)> PropValueList
{
get;
set;
}
ExistingVM()
: base(new Model())
{
// "old" vm initialization
initPropValueList();
}
ExistingVM(Model model)
: base(model)
{
// "old" vm initialization
initPropValueList();
}
private void initPropValueList()
{
PropValueList = new ObservableCollection<(string Prop, object Val)>()
{
(nameof(Prop1), Prop1),
// ...
}
}
}
Following the existing convention of the application:
// This piece of code is inside a Dialog Manager
public void ShowPropertiesDialog<T>(T propValueLikeVm)
{
if (propValueLikeVm is IPropertyGridVM)
{
// create the dialog
PropertyGridDialog dialog = new PropertyGridDialog();
// assign the datacontext
dialog.DataContext = propValueLikeVm;
// till here is all ok, VM is correctly initialized and contains what I expected
dialog.ShowDialog();
}
}
Now, in my general XAML dialog come the troubles:
<...
xmlns:vm="clr-namespace:ViewModels;assembly=ViewModels"
d:DataContext="{d:DesignData Type={x:Type vm:IPropertyGridVM},
IsDesignTimeCreatable=True}"
mc:Ignorable="d">
<!-- dialog styling and definition -->
<!-- Intellisense warns me of some problems in binding the PropValueList: "No DataContext value found for PropValueList"-->
<DataGrid ItemsSource="{Binding Path=PropValueList}">
<DataGrid.Columns>
<DataGridTextColumn Width="2*" Binding="{Binding Path=Prop}">
<!-- Text column styling -->
</DataGridTextColumn>
<DataGridTextColumn Width="2*" Binding="{Binding Path=Val}">
<!-- Text column styling -->
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
And it's right: at run-time I have binding errors that tell me that "The property Prop has not been found in the object of type ValueTuple'2" and "The property Val has not been found in the object of type ValueTuple'2", but I can't figure why.
Any hints?
The names Prop
and Val
are only really present before compilation. They are really named Item1
and Item2
. The compiler does some magic to let you use better names in the source. However, these are fields and not properties, and you might need properties to bind to in WPF. I would recommend just adding your own class:
public class PropVal : INotifyPropertyChanged{
public string Prop {get;}
public object Val {get;}
public PropVal(string prop, object val) => (Prop, Val) = (prop, val);
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I would expect that to be more reliable. Add a implicit or explicit conversion from the corresponding valuetuple if you wish.