【问题标题】:How to change something in View by ViewModel如何通过 ViewModel 更改视图中的某些内容
【发布时间】:2014-06-04 10:01:59
【问题描述】:

让我们看两个例子:

设置焦点

为了获得焦点,我们必须在代码隐藏中调用 方法 UIelement.Focus(),因此 MVVM 中的标准方法是创建 *behaviour`:

public static class FocusedBehavior
{
    public static bool GetIsFocused(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsFocusedProperty);
    }

    public static void SetIsFocused(DependencyObject obj, bool value)
    {
        obj.SetValue(IsFocusedProperty, value);
    }

    public static readonly DependencyProperty IsFocusedProperty =
      DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(FocusedBehavior), new UIPropertyMetadata(false, OnIsFocusedChanged));

    private static void OnIsFocusedChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if((bool)e.NewValue)
            (sender as UIElement).Focus();
    }
}

然后对于 每个 控件,我们希望能够设置焦点

<Button ... local:FocusedBehavior.IsFocused="{Binding SomeDependencyProperty}"/>

必须在 ViewModel 中为 每个 控件单独创建 bool SomeDependencyProperty。这样 ViewModel 可以通过设置其自身属性的值来更改 View 中的某些内容。

控制模糊

要设置模糊(see here),我们必须更改属性BlurEffect.Radius。这可以很简单

<Window.Effect>
    <BlurEffect Radius="{Binding SomeDependencyProperty}"/>
</Window.Effect>

int SomeProperty 所在的位置必须在 ViewModel 中针对每种情况亲自创建。

问题

还有其他方法可以在 View by ViewModel 中进行更改吗?

我想知道能够使用最合适的所有可能性。要设置焦点,我们必须使用 behaviour 方法来设置模糊简单绑定。还有更多可能吗?

非常通用的解决方案是让 ViewModel 知道 View,这样它就可以在处理命令时调用方法和使用属性,但这是个坏主意。正确的?虽然在这种情况下 View 可以扩展自身,使用公共属性和方法来实现某些结果(例如,设置焦点或设​​置模糊)。

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    您的问题似乎没有经过深思熟虑...通过更改视图模型中的属性,我可以通过使用IValueConverters、DataTriggers 来使视图中发生任何事情,附加属性等。例如,您不需要使用DependencyProperty 或附加属性来聚焦 UI 元素。您可以使用视图模型中的普通 bool 属性和 DataTrigger 来做到这一点,如下所示:

    <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding PropertyName}">
        <TextBox.Style>
            <Style TargetType="{x:Type TextBox}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsFocused}" Value="True">
                        <Setter Property="FocusManager.FocusedElement" Value="{Binding 
                            RelativeSource={RelativeSource Self}}" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>
    

    所以说真的,您似乎在问 WPF 中有什么可能,我认为这不是一个合理范围(或主题)的问题。要找出答案,最好阅读 MSDN 上的 Walkthrough: Getting Started with WPF 页面。

    但是,我认为您实际上可能会问如何通过 ViewModel 更改视图中的某些内容[特别是在使用 MVVM 时]。在这种情况下,我可以想到另一个使用的元素:不起眼的delegate,它使我们能够在相关视图模型之间传递值,或者在您的情况下,从视图模型到视图。

    使用这种机制,我们实际上可以[间接]从视图模型中调用视图中的 UI 方法,所以正如我之前所说的......我们几乎可以做任何事情 - MVVM 不会阻止我们使用 WPF 提供的任何东西。

    顺便说一句,你为什么在你的视图模型中使用DependencyPropertys?您不应该在视图模型中声明任何内容,因为它们用于 UI 元素......在视图模型中,您只需实现 INotifyPropertyChanged 接口并使用具有完全相同功能的普通旧 CLR 属性,但复杂性要低得多.


    更新>>>

    如前所述,DependencyProperty 是一个 UI 类,用于 UI 控件,例如 UserControl。在视图模型中使用它们完全是多余的,因为如前所述,我们可以在那里使用普通的 CLR 属性。如果您查看 MSDN 上的 DependencyProperty Class 页面,您将能够看到它拥有数百个公共成员......为什么在您不使用时为所有这些(在 RAM 中)付费 任何一个

    INotifyPropertyChanged 接口只添加了DependencyProperty,而是提供了相同的属性通知访问,但成本大大降低(没有数百个未使用的属性)。顺便说一句,您说您的所有视图模型都扩展了DependencyObject 类,我可以看出您误解了以下著名的错误:

    Binding 只能设置在DependencyObjectDependencyProperty

    我承认这是一个误导性错误,确实让新的 WPF 用户感到困惑。所以,我给你的建议是让你在你的视图模型中实现普通的 CLR 类型属性,并且扩展 DependencyObject 类......然后你也可以删除你的 UI 相关的 dll usings。此外,在使用 CLR 属性时,您需要正确实现 INotifyPropertyChanged interface 以便将它们“插入”WPF 属性更改通知框架。

    现在,您问如何使用delegates 将数据从视图模型传递到视图......不幸的是,这本身就是另一个完整的问题,我在这个问题上几乎没有时间了。因此,与其重复整个故事,我更愿意建议您阅读我在 Stack Overflow 上对 Passing parameters between viewmodelsHow to call functions in a main view model from other view models? 问题的回答,以获得解释和代码示例。

    恕我直言,如果您不让更多的 cmets 询问有关此问题的更多信息,我将不胜感激,因为这个答案现在已经足够长了。如果您还有其他问题,请提出新问题并提供尽可能多的代码/信息。

    【讨论】:

    • 如何使用delegate 向视图传递一些东西,你能解释一下吗?我正在使用附加属性,必须阅读DataTriggerValueConverter,例如谢谢。关于依赖属性,我的 ViewModel 是基于DependencyObject 的,所以使用propdp 生成的每个属性代码sn-p。在我的情况下,实施INotifyPropertyChanged 会增加什么?或者为什么它比DependencyProperty更好?
    • 我会尝试INotifyPropertyChange(尽管我已经不喜欢它必须手动将属性名称指定为stringpropdpd 是自动为我完成的)。你把我和delegate 混淆了,你为什么不直接说event? =D 仍然需要弄清楚 View 如何使用 ViewModel 事件(给定的链接涵盖 ViewModel ViewModel 事件和 View 中的 Command 绑定,我还不太了解是什么)。谢谢。
    • 我会尝试 INotifyPropertyChange(尽管我已经不喜欢它,因为必须手动将属性名称指定为字符串 如果您查看了相关的链接页面,您应该看到您 不需要在 .NET 4.5 (CallerMemberName) 中使用 string 名称。你把我和委托搞混了,你为什么不直接说事件?因为delegate 不是 event - 它没有EventArgs,我们可以为它们定义任何我们喜欢的输入参数。
    • INotifyPropertyChange 有效,CallerMemberName 的东西简直太棒了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    • 1970-01-01
    • 2019-11-05
    相关资源
    最近更新 更多