在WPF应用程序中使用MVVM / MVVMLight时如何与UI元素进行交互

时间:2017-10-30 19:21:05

标签: c# wpf mvvm mvvm-light

根据下面的代码,我希望能够在点击Button 2时更改Button 1的背景颜色。

XAML文件

    <Grid>
        <Button x:Name="Button1" 
                Content="Button 1" 
                Command="{Binding Button1Command}"/>

        <Button x:Name="Button2" 
                Content="Button 2"/>
    </Grid>

ViewModel文件

public class MyViewModel : ViewModelBase
{
    public ICommand Button1Command{get;private set;}

    public MyViewModel(){
        Button1Command = new RelayCommand(() => button1_Click());
    }

    private void button1_Click()
    {
        Console.WriteLine("Button 1 clicked");

        // how can I change the background color of Button 2 here
        this.Dispatcher.Invoke(() => {
           Button2.Background = Brushes.Red;
        });
    }
}

3 个答案:

答案 0 :(得分:4)

除了pm_2提到的内容之外,您还可以利用MVVMLight的Messenger课程。 VM可以发送视图接收的消息以更改背景。

public class ChangeBackgroundMessage
{
    public Brush TheColor { get; set; } 
} 

然后在你的VM中:

Button1Command = new RelayCommand(() => ExecuteButtonCommand());

....

private void ExecuteButtonCommand()
{
    Messenger.Default.Send<ChangeBackgroundMessage>(new ChangeBackgroundMessage { TheColor = Brushes.Red } );
} 

并在您的视图中:

public partial class MyView : UserControl
{
    public MyView()
    {
         InitializeComponent();
         Messenger.Default.Register<ChangeBackgroundMessage>(this, m => ReceiveChangeBackgroundMessage(m);
    } 

    private void ReceiveChangeBackgroundMessage(ChangeBackgroundMessage m)
    {
          // If you need to ensure this executes only on UI thread, use the
          // DispatcherHelper class

          DispatcherHelper.CheckBeginInvokeOnUI(() => button2.Background = m.TheColor);
    }

}

另一种选择是拥有一个&#34;查看服务&#34; View注册了它的ViewModel。例如:

public interface IMySpecificViewService
{ 
    void ChangeButtonColor(Brush color);
} 

在VM中:

public IMySpecificViewService ViewService { get; set; } 

并在视图中

public partial class MyView : UserControl, IMySpecificViewService
...
public MyView()
{ 
    var vm = (MyViewModel)this.DataContext;
    vm.ViewService = (IMySpecificViewService)this;
} 

public void ChangeButtonColor(Brush color)
{
    Button2.Background = color;
}  

可以在VM的命令处理程序中调用:

private void ExecuteButtonCommand()
{
    ViewService?.ChangeButtonColor(Brushes.Red);
} 

当我无法直接绑定到VM中的某个属性时,我发现我使用这些方法(或者我不想在VM中查看任何View特定内容)并且我需要更细粒度控制操纵控件。

答案 1 :(得分:3)

有两种方法可以解决这个问题 - 第一种方法是简单地将Button2的背景颜色绑定到viewmodel上的属性。您可以将它作为画笔从视图模型中公开;虽然与MVVM更一致的方式是创建一个值转换器。

Button2的背景虽然与Button1相关联,但实际上与按下Button1时已更改的状态相关联;然后,值转换器将状态(它是ViewModel的域)与颜色(视图的域)进行映射。

这样做就意味着您可以在button1的view model命令中更改状态,但不必涉及button1_click事件,因为现在不需要。

This question说明了如何实现这一目标。

答案 2 :(得分:1)

首先,您需要在视图模型中声明一个属性,该属性将控制背景色以及一个命令处理程序,按钮可以调用该命令处理程序来切换背景色。这似乎有些冗长,但您很快就对MVVM熟悉了,如果确实困扰您,可以使用一些框架将其最小化。所以这是主要的视图模型:

public class MainViewModel : ViewModelBase
{
    #region Background Color Flag

    private bool _Flag;
    public bool Flag
    {
        get { return this._Flag; }
        set
        {
            if (this._Flag != value)
            {
                this._Flag = value;
                RaisePropertyChanged(() => this.Flag);
            }
        }
    }

    #endregion Background Color Flag

    #region Button Command Handler

    private ICommand _ButtonCommand;
    public ICommand ButtonCommand
    {
        get { return this._ButtonCommand = (this._ButtonCommand ?? new RelayCommand(OnButtonPressed)); }
    }

    private void OnButtonPressed()
    {
        this.Flag = !this.Flag;
    }

    #endregion Button Command Handler

    public MainViewModel()
    {
    }

}

MVVM的目标之一是使视图与视图模型之间的耦合尽可能松散。 Button的命令绑定应该非常简单,但是要设置第二个按钮的背景,可以使用DataTriggers:

<StackPanel Orientation="Vertical">

    <Button Content="Toggle Background" HorizontalAlignment="Left" VerticalAlignment="Top"
        Command="{Binding ButtonCommand}" />

    <Button Content="Hello World!" HorizontalAlignment="Left" VerticalAlignment="Top">
        <Button.Style>
            <Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Flag}" Value="False">
                        <Setter Property="Background" Value="Red" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding Flag}" Value="True">
                        <Setter Property="Background" Value="Green" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>

</StackPanel>

这将导致第二个按钮的背景在您单击第一个按钮时在红色和绿色之间切换:

enter image description here