我有一个DataTemplate需要在ItemsControl的容器上设置IsSelected属性(例如TreeViewItem,ListViewItem或ComboBoxItem)。但是,在传递给它之前,它不知道容器的类型。由于IsSelected不是公共基类或接口的一部分,也不是AddOwner向各个类注册的常见依赖属性(Duh,MS !!! WTF不是?!!),我最终得到了这个混乱。
if (container is TreeViewItem) {
(container as TreeViewItem).IsSelected = true;
return;
}
if (container is ListBoxItem) {
(container as ListBoxItem).IsSelected = true;
return;
}
if (container is ComboBoxItem) {
(container as ComboBoxItem).IsSelected = true;
return;
}
...这不仅是冗长的,而且如果我使用不同的使用不同容器类型的ItemsControl,则需要我修改它!不好!</ p>
当然,我可以通过将这个逻辑放在扩展方法(该死的C#,因为没有扩展属性!!)中调用IsContainerSelected和SetContainerSelected并将它们放在UIElement上,然后在上面移动上面的代码来增强它,但它只是制作外面的整洁者。内部仍然是一团糟。
我唯一的另一个想法是使用反射并查找IsSelected属性并使用它,如果找到,但我总是对做这样的事情很谨慎。但是,由于没有通用的接口或基类,我不确定我在这里有一个选择。
对于上下文,我在几个不同的ItemsControl之间共享一个复杂的数据模板,模板本身具有可以接收焦点的控件,如复选框和文本框。但是,当这些控件通过鼠标获得焦点时,基础容器项目不会被选中,之前选择的任何内容仍然是如此。
我的解决方法是使用附加行为,利用预览事件在焦点发生之前拦截焦点,并相应地设置基础项,这在我硬编码TreeViewItem或ListBoxItem等时效果很好,但我不喜欢我不想对类型进行硬编码,因为控件不应该真正关心。这就是崩溃的部分。
唉!!!为什么MS只注册相同的附加属性或者至少创建一个ISelectableContainer接口?!!
答案 0 :(得分:2)
我已经阅读了你的答案,这确实有意义 - 在你的情况下,IsSelected
显然可能是ViewModel的一部分,而且这似乎是你案例中最好的解决方案。
但您要求进一步解释C#动态功能。 C#4.0现在具有一些动态功能,这使我们能够创建只能在Python,Ruby或JavaScript等语言中实现的代码。当然,这有其成本 - dynamic
滥用不仅会使代码变慢,而且会更加混乱 - 因为您会丢失编译时错误和智能感知。
我写了一个简单的例子,你可以更好地理解它:
public class ClassOne
{
public int SameProperty { get; set; }
}
public class ClassTwo
{
public int SameProperty { get; set; }
}
public class ClassThree
{
public string SameProperty { get; set; }
}
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
dynamic wrapper = new ClassOne();
wrapper.SameProperty = 5;
wrapper = new ClassTwo();
wrapper.SameProperty = 15;
wrapper = new ClassThree();
wrapper.SameProperty = "Now it is a string!";
// And now a run-time error...
wrapper.AnotherProperty = "And this won't work...";
}
}
正如您所看到的,wrapper
没有任何明确的类型 - dynamic
引用将允许任何类型的方法或属性调用,因为实际绑定仅在运行时进行,而不是编译时。
当然,这个例子非常幼稚,但有时动态代码可能很有用 - 它是避免显式反射或避免基于类型的长if...else
语句的好选择(如上面的代码片段)。
答案 1 :(得分:1)
我不确定我是否完全理解你的问题,但是你可以尝试在模型中添加一个IsSelected布尔值,然后将该属性绑定到它所包含的Item控件。这样,你只需要担心设置模型中的属性,无论容器如何。
答案 2 :(得分:0)
Per @mdm20的答案,他建议修改ViewModel,这当然是你想要做的。然而,这是纯粹与视图相关的问题(与键盘导航相关),并且根本没有反映在ViewModel中,也不应该在这种情况下反映出来。
但这给了我一个想法!由于我正在使用自定义控件来渲染项目,无论哪个项目控件(通过其数据模板)都被添加到该项目中,该控件自然 具有多个实例(所有这些实例都指向同一个实例) ViewModel实例),这就是我想要的!
因此,我不是将IsSelected添加到ViewModel,而是将其添加到用户控件本身,然后我只是绑定到数据模板中的相应ItemsControl,我知道。然后,我可以根据需要在代码隐藏中为用户控件设置IsSelected属性(即在预览鼠标事件期间等),并且基础ItemsControl会做出相应的响应!效果很好并且保持ViewModel清洁,因为模型和视图模型都不需要知道它。 IsSelected仍然纯粹在UI中,在这种特殊情况下它应该是!