【问题标题】:wpf: changing the value of a Dependency Property from viewmodelwpf:从视图模型更改依赖属性的值
【发布时间】:2013-03-15 00:55:05
【问题描述】:

我有一个自定义用户控件,它具有字符串类型的自定义依赖属性。它应该双向绑定到一个文本框,该文本框位于周围的应用程序中,如下所示:

<Textbox Name="TextBoxInHostApp"/>
<ctrl:MyControl 
     ....
         MyString = "{Binding ElementName=TextBoxInHostApp, Path=Text, Mode=TwoWay}"
</ctrl:MyControl>

问题:

控件还应该具有显示依赖属性值的功能!

在 MyControl 中,我有一个简单的字符串属性“Name”,它应该更新 MyString 的依赖属性值。

所以通常情况下,如果 MyString 未绑定到外部世界,您将在使用控件的 xaml 中进行编码:

<Textbox Name="TextBoxInHostApp"/>
<ctrl:MyControl 
     ....
         MyString = "{Binding Name}"
</ctrl:MyControl>

但这在我的情况下不可用。

尝试 1: 我在用户控件 xaml 中使用了触发器:

<UserControl.Style>
    <Style>
        <Style.Setters>
            <Setter Property="local:MyControl.MyString" Value="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
        </Style.Setters>
    </Style>

现在这个触发器不起作用了,因为这里已经设置了 dp 的“本地值”:

MyString = "{Binding ...

如果我注释掉:

<!--<MyString = "{Binding ...

...触发器工作正常,因为“本地值”没有设置。

下一次尝试: 我在 MyControls 视图模型中尝试了艰难的方法和代码:

public string Name
{
    get
    {
        return _name;
    }
    set
    {
        _name = value;
         RaisePropertyChanged("Name");

         MyOwningUICtrl.SetValue(MyControl.MyStringProperty, value); // ??
    }
}

我必须通过一个 DependencyObject 来调用一个 SetValue,这对 MVVM 来说是不愉快的。这种情况一定有更好的解决方案吗?

【问题讨论】:

    标签: wpf dependency-properties


    【解决方案1】:

    如果您在 Textbox 和 Mycontrol 中显示相同的值...为什么不将它们绑定到该属性? :

    <Textbox Name="TextBoxInHostApp" Text="{Binding Name}"/>
    <ctrl:MyControl 
         ....
             MyString="{Binding Name}"
    </ctrl:MyControl>
    

    【讨论】:

    • 关键是应用程序无权访问“名称”属性。由文本框表示的应用程序字符串应该通过“MyString”dp修改用户控件“。在“MyString”中设置“Name” - ChangedCallback在这里不相关。问题是“Name”被允许修改“ MyString" dp,因此,如果控件内部的“名称”发生更改,应用程序文本框中的文本也应该更改。
    【解决方案2】:

    这是设计使然

    根据 WPF 的 Dependency Property Precedence List,在 &lt;Tag&gt; 中本地指定的值将始终优先于在 StyleTrigger 中指定的值

    一个常见的问题是在&lt;Tag&gt; 中设置一个值,然后尝试在Style.Trigger 中更改它。解决方案是在&lt;Style&gt; 中而不是&lt;Tag&gt; 中将默认值设置为&lt;Setter&gt;,这将允许触发值优先于样式值

    <Style>
        <!-- Default Value
        <Setter Property="local:MyControl.MyString" 
                Value="{Binding ElementName=TextBoxInHostApp, Path=Text, Mode=TwoWay}"/>
    
        <Style.Triggers>
            <Trigger ...>
                <!-- Triggered Value -->
                <Setter Property="local:MyControl.MyString" 
                        Value="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
            </Trigger>
        </Style.Setters>
    </Style>
    

    但我实际上并没有在任何地方看到您的帖子中定义的触发器,所以我不确定这是否是您的问题。

    我有点不清楚您的代码的确切结构,但请记住,依赖属性有点像指向另一个值的指针。您可以将其设置为指向TextBox.Text,或指向DataContext.Name,但不能同时指向两者。

    如果您想将TextBox.TextMyUserControl.MyString 都设置为指向DataContext.Name,那么您应该将这两个属性都绑定到"{Binding Name}",例如Natxo suggests

    或者,如果您尝试将MyString 绑定到UserControl.Name,如果MyString 未在&lt;ctrl:MyControl ...&gt; 标记中指定,那么您可以尝试使用PriorityBinding(例如here),或者直接公开外部世界的Name 属性让外部元素使用控件,如&lt;ctrl:MyControl Name="{Binding ...}" /&gt;

    【讨论】:

    • 1st) 你说得对,我使用的是样式设置器,而不是触发器。 2nd) textbox.text 应该是绑定到 MyString dp 的属性。设计事实是,dp 的值可以通过两种方式更改:从应用布局上的文本框和自定义控件内的文本框。
    • 我刚刚测试的非智能解决方案:MyOwningUICtrl.SetValue(MyControl.MyStringProperty, value); // ?? ...工作正常,但我认为这不是好的编程
    • @deafjeff 请记住,绑定就像指针——它们指向某处的DataContext 中的值。听起来您希望您的MyString 依赖属性指向UserControl 之外的某个值(例如{Binding Name},并且您希望TextBox.Text 指向UserControl.MyString。所以您的最终结果是&lt;TextBox Text={Binding ElementName=MyUserControl, Path=MyString}" /&gt;&lt;ctrl:MyUserControl MyString="{Binding Name}" /&gt;
    • 现在这似乎是合理的!谢谢!!
    猜你喜欢
    • 1970-01-01
    • 2014-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多