英文:
Binding named tuple to XAML DataGrid
问题
在我的WPF应用程序中,我有许多视图模型,现在需要一个像"属性-值"一样的网格。
我尝试构建一个通用的视图模型,可以“注入”到现有的视图模型中,以便我可以在新窗口中使用它们,几乎不需要任何努力。为此,我编写了一个带有可观察集合的简单接口:
public interface IPropertyGridVM
{
ObservableCollection<(string Prop, object Val)> PropValueList { get; set; }
}
在需要新属性值网格的一个视图模型中:
public class ExistingVM : ViewModelBase<Model>, IPropertyGridVM
{
public ObservableCollection<(string Prop, object Val)> PropValueList { get; set; }
ExistingVM() : base(new Model())
{
// "旧"视图模型初始化
initPropValueList();
}
ExistingVM(Model model) : base(model)
{
// "旧"视图模型初始化
initPropValueList();
}
private void initPropValueList()
{
PropValueList = new ObservableCollection<(string Prop, object Val)>()
{
(nameof(Prop1), Prop1),
// ...
};
}
}
遵循应用程序的现有约定:
// 这段代码位于对话框管理器内
public void ShowPropertiesDialog<T>(T propValueLikeVm)
{
if (propValueLikeVm is IPropertyGridVM)
{
// 创建对话框
PropertyGridDialog dialog = new PropertyGridDialog();
// 分配数据上下文
dialog.DataContext = propValueLikeVm;
// 到这里都没问题,视图模型已正确初始化并包含我预期的内容
dialog.ShowDialog();
}
}
现在,在我的通用XAML对话框中出现问题:
<!-- Intellisense提醒我在绑定PropValueList时存在一些问题:“未找到PropValueList的DataContext值”-->
<DataGrid ItemsSource="{Binding Path=PropValueList}">
<DataGrid.Columns>
<DataGridTextColumn Width="2*" Binding="{Binding Path=Prop}">
<!-- 文本列样式 -->
</DataGridTextColumn>
<DataGridTextColumn Width="2*" Binding="{Binding Path=Val}">
<!-- 文本列样式 -->
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
这是正确的:在运行时,我有绑定错误,告诉我“在ValueTuple'2类型的对象中未找到Prop属性”和“在ValueTuple'2类型的对象中未找到Val属性”,但我无法弄清楚原因。
有什么提示吗?
英文:
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?
答案1
得分: 1
以下是翻译好的内容:
"Prop" 和 "Val" 这两个名称只在编译之前真正存在。它们实际上被命名为 "Item1" 和 "Item2"。编译器进行了一些魔法操作,以便您在源代码中使用更好的名称。但是,这些是字段而不是属性,您可能需要属性来在 WPF 中进行绑定。我建议您添加自己的类:
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));
}
}
我期望这将更加可靠。如果您愿意,可以从相应的 ValueTuple 中添加隐式或显式转换。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论