【问题标题】:Expose child properties of user control directly as property of user control将用户控件的子属性直接公开为用户控件的属性
【发布时间】:2015-06-18 07:13:21
【问题描述】:

在不破坏 MVVM 的情况下,有没有办法在用户控件中公开子控件的某些属性,以便窗口或其他使用它的用户控件可以直接访问这些属性?

例如,我有一个用户控件,它有一个设置了网格视图列、标题的列表视图,并绑定到一个视图模型。但是用户控件中的列表视图已经选择了项目属性,因此我想向主机公开,而不必执行 usercontrol.customListView.property 之类的操作。还是我应该这样做?我想只使用 usercontrol.property,省略 customListView。也许我应该只在返回列表视图控件属性的用户控件代码中创建属性,我希望这些属性直接附加到用户控件?

我觉得后一种选择并没有真正破坏 MVVM,因为它们暴露给主机进行交互,与视图本身没有真正的关系。任何建议将不胜感激。

编辑:事实上,我真的很想在用户控件上直接有一个 SelectedItem 属性,它不是 ListViewItem 或对象,但实际上包含的数据类型是这样的:

public MyDataType SelectedItem {
    get {
        return customListView.SelectedItem as MyDataType;
    }
}

这在 MVVM 中是否允许?因为我看不到如何在 ViewModel 中拥有它,所以它似乎必须在后面的部分类代码中。

【问题讨论】:

  • 只需在您的控件上添加 DepandencyProperty 并在子控件上绑定/重新绑定它 (Binding RelativeSource{FindAncestor})(顺便说一句,也许 OT,WPF 数据网格上的 .columns 不可绑定)。看不到 MVVM 的任何问题 - 可以说 ComboBox 正在使用其他控件,而您在 MVVM 中使用它没有任何问题:)
  • 这个问题与MVVM无关。
  • "我有一个用户控件,它有一个设置了 gridviewcolumns、标题的列表视图,并绑定到一个视图模型。" 是的,这就是你遇到问题的原因.您的 UserControl 中不应该有任何 ViewModel。您应该在表面上公开您需要的所有属性,然后将您的 UC 子控件绑定到这些属性。如果您必须在 UC 中执行 UI 逻辑,请使用代码隐藏。不要创建设计为在 UC 内部使用的 VM。 TextBox 有 TextBoxViewModel 吗?不。将您的 UC 视为控件,而不是逻辑的子集。
  • @Sheridan 你读过这个问题了吗?我正在使用 MVVM 并希望在用户控件上公开一些内容。我不知道这是否会影响它的 MVVM 方面,或者我们是否应该在实践中避免这种情况,但我只是不知道如何推迟对视图模型的暴露。
  • 是的,我读过这个问题。你的设计对你不利。从您的第二次澄清来看,如果您的 UC 包含另一个不同的过程,它似乎太大了。如果 UC 很小,则最有效,设计为针对整体 MVVM 设计的一部分而设计的专用控件(如 TextBox 编辑字符串,PersonUserControl 可能编辑 Person 模型)。如果您使用 UC 来包含大量 UI,您会发现自己处于上述评论中描述的情况。

标签: c# wpf mvvm


【解决方案1】:

当您想将重复的内容放入UserControl 时,这是很常见的任务。最简单的方法是当您不为 UserControl 创建专门的 ViewModel 时,而是制作自定义控件(为简单起见,使用 UserControl 构建)。最终结果可能如下所示

<UserControl x:Class="SomeNamespace.SomeUserControl" ...>
    ...
    <TextBlock Text="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}" ...>
</UserControl>

.

public partial class SomeUserControl : UserControl
{
    // simple dependency property to bind to
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(SomeUserControl), new PropertyMetadata());

    // has some complicated logic
    public double Value
    {
        get { return (double)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(double), typeof(SomeUserControl),
        new PropertyMetadata((d, a) => ((SomeUserControl)d).ValueChanged()));
    private void ValueChanged()
    {
        ... // do something complicated here
            // e.g. create complicated dynamic animation
    }

    ...
}

在包含窗口中的使用将如下所示

<l:SomeUserControl Text="Text" Value="{Binding SomeValue}" ... />

如您所见,SomeValue 绑定到 Value 并且没有 MVVM 违规。

当然,如果视图逻辑复杂或需要太多绑定,您可以创建一个合适的ViewModel,并且允许 ViewModel 直接通信(通过属性/方法)更容易。

【讨论】:

  • 在视图模型中发生的视图本身有自定义逻辑。例如,我正在研究的是一个列表视图,其中包含公司数据类型的各种列。我从 Prism 扩展了 BindableBase,并且通常使用 SetProperty() 在可能的情况下设置属性。如果尚未注册,您是否知道 SetProperty 是否会代表该类自动注册 DependencyProperty?我还想直接将 SelectedItem 公开为 Company 类型,并为 Company 类型公开一个 SelectionChanged 事件。
猜你喜欢
  • 2018-11-07
  • 1970-01-01
  • 2011-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-16
相关资源
最近更新 更多