Reputation: 10738
Based on my code below, I want to be able to change the background color of a Button 2
when Button 1
is clicked.
<Grid>
<Button x:Name="Button1"
Content="Button 1"
Command="{Binding Button1Command}"/>
<Button x:Name="Button2"
Content="Button 2"/>
</Grid>
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;
});
}
}
Upvotes: 4
Views: 1507
Reputation: 16119
First of all you need to declare a property in your view model that will control the background color as well as a command handler which a button can call to toggle it. This might seem a little verbose but you soon get used to that with MVVM, and there are frameworks you can use to minimize that if it really bothers you. So here's the main view model:
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()
{
}
}
One of the objectives of MVVM is to have as loose coupling between the view and the view model as possible. The Button's command binding should be fairly straightforward, but to set the background of the second button you can use 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>
This will cause the second button's background to toggle between red and green as you click the first button:
Upvotes: 1
Reputation: 1322
In addition to what pm_2 mentioned, you could take advantage of MVVMLight's Messenger
class. The VM can send a message that is received by the View to change the background.
public class ChangeBackgroundMessage
{
public Brush TheColor { get; set; }
}
And then in your VM:
Button1Command = new RelayCommand(() => ExecuteButtonCommand());
....
private void ExecuteButtonCommand()
{
Messenger.Default.Send<ChangeBackgroundMessage>(new ChangeBackgroundMessage { TheColor = Brushes.Red } );
}
and in your View:
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);
}
}
Yet another alternative would be to have a "view service" that the View registers with it's ViewModel. For example:
public interface IMySpecificViewService
{
void ChangeButtonColor(Brush color);
}
In VM:
public IMySpecificViewService ViewService { get; set; }
and in View
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;
}
which can be called in your VM's command handler:
private void ExecuteButtonCommand()
{
ViewService?.ChangeButtonColor(Brushes.Red);
}
I find I use these approaches when I can't directly bind to a property in the VM, (or I don't want to bleed any View specific stuff in the VM) and I need more fine grained control over manipulating the controls.
Upvotes: 4
Reputation: 16685
There are two approaches to this that spring to mind - the first is to simply bind the background colour of Button2 to a property on the viewmodel. You could expose this from the view model as a brush; although the way that is more consistent with MVVM would be to create a value converter.
The idea being that the background of Button2, despite being linked to Button1, is actually linked to a state that has changed when Button1 is pressed; the value converter then maps the state (which is the domain of the ViewModel) with the colour (the domain of the view).
Doing is this way, means that you can change the state in the view model command of button1, but not have to involve the button1_click event, as it is now unnecessary.
This question illustrates how you might achieve this.
Upvotes: 3