I'm new to WPF and while I'm trying to learn it I came across MVVM framework. Now I'm trying to implement it with a simple application i made which imports and displays an image.
XAML:
<Window x:Class="mvvmSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="1024" Height="768">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Header="Imported Picture">
<Image x:Name="_image" Stretch="Fill"/>
</GroupBox>
<Button Height="50" Grid.Row="1" Content="Import Picture" Click="Button_Click"/>
</Grid>
</Window>
Code Behind:
using Microsoft.Win32;
using System;
using System.Windows;
using System.Windows.Media.Imaging;
namespace mvvmSample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog open = new OpenFileDialog();
open.DefaultExt = (".png");
open.Filter = "Pictures (*.jpg;*.gif;*.png)|*.jpg;*.gif;*.png";
if (open.ShowDialog() == true)
_image.Source = new BitmapImage(new Uri(open.FileName));
}
}
}
I watched alot of tutorial on mvvm for beginners and read alot of articles about it and i grasp the concept behind it. With my application I'm assuming the view would be the same as what i have but without using events but rather using command binding for both source and button. For model, I would assume I should have an image property but I'm not sure if it should get and set the filepath or the image itself. The View Model would then contain functions for both image retrieval (OpenFileDialog) and command for the button. Are my assumptions correct or is there a better way of transforming this application into mvvm. A sample coding would be great so I can analyze it.
Thanks in advance,
In the ViewModel you should define the logic you want to execute when you click the button. To do that, you need to use a command. My suggestion is use a RelayCommand, which is a generic command:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
//[DebuggerStepThrough]
/// <summary>
/// Defines if the current command can be executed or not
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
Assuming that you are using the RelayCommand as defined above you need to supply it with one or two delegates, one that returns a bool which determines whether the command is in a valid state to be run, and a second which returns nothing and actually runs the command. If you don't supply a CanExecute delegate then the command will consider that it is always in a valid state.
In your ViewModel, you also need a property to save te Image path. This property will be bind with the Source property of the Image that you have in the View. So, your ViewModel class would be like this:
public class MainViewModel: INotifyPropertyChanged
{
private string imagePath;
public string ImagePath
{
get { return imagePath; }
set
{
imagePath = value;
SetPropertyChanged("ImagePath");
}
}
ICommand _loadImageCommand;
public ICommand LoadImageCommand
{
get
{
if (_loadImageCommand == null)
{
_loadImageCommand = new RelayCommand(param => LoadImage());
}
return _loadImageCommand;
}
}
private void LoadImage()
{
OpenFileDialog open = new OpenFileDialog();
open.DefaultExt = (".png");
open.Filter = "Pictures (*.jpg;*.gif;*.png)|*.jpg;*.gif;*.png";
if (open.ShowDialog() == true)
ImagePath = open.FileName;
}
#region Property Changed Event Handler
protected void SetPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion Property Changed Event Handler
}
With this ViewModel you don't need to do anything in the code begin of your View. You just instantiate your ViewModel in the window's resources and set the DataContext property of the root grid. After that, you can bind the properties and commands with the proper controls:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Width="1024" Height="768">
<Window.Resources>
<wpfApplication1:MainViewModel x:Key="MainViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource MainViewModel}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Header="Imported Picture">
<Image x:Name="_image" Stretch="Fill" Source="{Binding ImagePath}"/>
</GroupBox>
<Button Height="50" Grid.Row="1" Content="Import Picture" Command="{Binding LoadImageCommand}" />
</Grid>
To Octavioccl
I did use my own namespace in my coding as so:
<Window x:Class="mvvmSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mvvmSample="clr-namespace:mvvmSample"
xmlns:vm="clr-namespace:mvvmSample.ViewModel"
Title="MainWindow" Width="1024" Height="768">
<Window.Resources>
<vm:MainViewModel x:Key="MainViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource MainViewModel}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Header="Imported Picture">
<Image x:Name="_image" Stretch="Fill" Source="{Binding ImagePath}" />
</GroupBox>
<Button Height="50" Grid.Row="1" Content="Import Picture" Command="{Binding LoadedImageCommand}"/>
</Grid>
As you can see I have both:
xmlns:mvvmSample="clr-namespace:mvvmSample"
xmlns:vm="clr-namespace:mvvmSample.ViewModel"
I added the later since I cannot access the MainViewModel in Window.Resources without it and mvvmSample only have App as choice. The program runs but it doesn't do anything it just shows the UI and if i click the button nothing happens. I placed breakpoints in several places like in RelayCommand and MainWindow code behind to be able to observe the problem, but it just initialize components and shows the UI.
Update:
I was able to solve this problem which was totally my fault. I wrote the wrong function name in binding instead of LoadImage I have LoadedImage thats why my button click doesnt activate "DUH". Thanks for all your help.
Related
I have delved into the magic and mystery of WPF and Binding. It was going OK then I hit a brick wall and need to ask those much cleverer than me for help please.
I cut this back to a simple app removing all the other items in my code. The UI has a text box and a label. When the text in the textbox changes then I want to update the label. Somewhere I am missing a link and I guess it is the binding as I never seem to get into the set. Here is the code
Mainwindow.xaml.cs
using System.ComponentModel;
using System.Windows;
namespace Databinding3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string myBindedVal = "....";
public MainWindow()
{
InitializeComponent();
}
//Create properties for our variable _myBindedVal
public string MyBindedVal
{
get => myBindedVal;
set
{
NotifyPropertyChanged(nameof(MyBindedVal));
myBindedVal = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (propertyName != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Mainwindow.xml
<Window x:Class="Databinding3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Databinding3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBox x:Name="txtbx_name" Text="Textbox" HorizontalAlignment="Center" Height="57" TextWrapping="Wrap" VerticalAlignment="Center" Width="594"/>
<Label Content="{Binding MyBindedVal, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" HorizontalAlignment="Center" Height="44" Grid.Row="1" VerticalAlignment="Center" Width="594"/>
</Grid>
</Window>
Thanks for your help
You did not bind the Text property of the TextBox. It should look like shown below, where the UpdateSourceTrigger ensures that the source property is updated immediately when you type into the TextBox.
<TextBox Text="{Binding MyBoundVal, UpdateSourceTrigger=PropertyChanged}" .../>
The above Binding does not explicitly specify a source object, and therefore uses the Window's DataContext as source. Set the DataContext like this:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
The Label Binding would then just be
<Label Content="{Binding MyBoundVal}" .../>
Be aware that you would typically use a TextBlock, not a Label, to show text:
<TextBlock Text="{Binding MyBoundVal}" .../>
The execution order in the property setter is also important. Assign the backing field value before firing the PropertyChanged event.
public string MyBoundVal
{
get => myBoundVal;
set
{
myBoundVal = value;
NotifyPropertyChanged(nameof(MyBoundVal));
}
}
Finally, the NotifyPropertyChanged method should look like shown below. Testing the propertyName argument is pointless, but you should test the PropertyChanged event for null, usually by using the null-propagation operator ?.:
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I need a "Remove" button for each item in the listbox of a UserControl. I am trying to use a ListBox Template.
<UserControl.Resources>
<DataTemplate x:Key="ListBoxItemTemplate">
<Grid>
<TextBlock x:Name="TB" Height="23" Text="" VerticalAlignment="Top" />
<Button Margin="500,0,0,0" Width="80" Click="Button_Click">remove</Button>
</Grid>
</DataTemplate>
</UserControl.Resources>
<ListBox x:Name="listbox" ItemTemplate="{StaticResource ListBoxItemTemplate}" SelectionChanged="ListBox_SelectionChanged"/>
How to make the text content of each item specified by the back-end code as shown below and how does the "remove" button remove its corresponding item?
Thanks.
listbox.Items.Add("111");
listbox.Items.Add("222");
Simpliest solution would be to use the ICommand system of WPF.
Using this, you can bind your current item to the commands parameter, and use that in code beind.
(I am using window, not user control, but you can just change that..
This is the WPF:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" x:Name="Window" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="ListBoxItemTemplate">
<Grid>
<TextBlock Height="23" Text="{Binding}" VerticalAlignment="Top" />
<Button Margin="500,0,0,0" Width="80" CommandParameter="{Binding}" Command="{Binding ElementName=Window, Path=OnClickCommand}">remove</Button>
</Grid>
</DataTemplate>
</Window.Resources>
<ListBox x:Name="listbox" ItemTemplate="{StaticResource ListBoxItemTemplate}" />
</Window>
{Binding} means to bind the current object the template is used on (in this instance, the string in my implementation.
The OnClickCommand is what does the magic.
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
listbox.Items.Add("asdf");
listbox.Items.Add("fdsfsadf");
listbox.Items.Add("dfsd");
listbox.Items.Add("a sdfas");
listbox.Items.Add("asdgas g");
//This is the command that gets executed.
OnClickCommand = new ActionCommand(x => listbox.Items.Remove(x));
}
public ICommand OnClickCommand { get; set; }
}
//This implementation of ICommand executes an action.
public class ActionCommand : ICommand
{
private readonly Action<object> Action;
private readonly Predicate<object> Predicate;
public ActionCommand(Action<object> action) : this(action, x => true)
{
}
public ActionCommand(Action<object> action, Predicate<object> predicate)
{
Action = action;
Predicate = predicate;
}
public bool CanExecute(object parameter)
{
return Predicate(parameter);
}
public void Execute(object parameter)
{
Action(parameter);
}
//These lines are here to tie into WPF-s Can execute changed pipeline. Don't worry about it.
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
}
Note that it doesn't matter whether you use string or any other type of object, because {Binding} gets the current object that is in the ListBox.Items list
I have the following XAML:
<Window x:Class="ListBoxTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ListBoxTest"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:Model />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Items}" Grid.Row="0"/>
<Button Content="Add" Click="Button_Click" Grid.Row="1" Margin="5"/>
</Grid>
</Window>
and the following code for the Model class, which is put into main window's DataContext:
public class Model : INotifyPropertyChanged
{
public Model()
{
items = new Dictionary<int, string>();
}
public void AddItem()
{
items.Add(items.Count, items.Count.ToString());
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Items"));
}
private Dictionary<int, string> items;
public IEnumerable<string> Items { get { return items.Values; } }
public event PropertyChangedEventHandler PropertyChanged;
}
and main window's code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var model = this.DataContext as Model;
model.AddItem();
}
}
When pressing the button, the contents of the list are not being updated.
However, when I change the getter of the Items property to this:
public IEnumerable<string> Items { get { return items.Values.ToList(); } }
it starts to work.
Then, when I comment out the part which sends PropertyChanged event it stops working again, which suggests that the event is being sent correctly.
So if the list receives the event, why can't it update its contents correctly in the first version, without the ToList call?
Raising the PropertyChanged event for the Items property is only effective if the property value has actually changed. While you raise the event, the WPF binding infrastructure notices that the collection instance returned by the property getter is the same as before and does nothing do update the binding target.
However, when you return items.Values.ToList(), a new collection instance is created each time, and the binding target is updated.
I have an UserControl defined as follows:
<UserControl x:Class="Speaker.View.Controls.Prompt"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:conv="clr-namespace:Speaker.View.Converters"
mc:Ignorable="d" Height="Auto" Width="300" x:Name="PromptBox">
<UserControl.Resources>
<conv:VisibilityConverter x:Key="VConverter" />
</UserControl.Resources>
<Border Background="White" Padding="10" BorderThickness="1"
BorderBrush="Gray" CornerRadius="10" Height="80"
Visibility="{Binding Path=Show, ElementName=PromptBox,
Converter={StaticResource VConverter}}"
UseLayoutRounding="True">
<Border.Effect>
<DropShadowEffect BlurRadius="20" RenderingBias="Quality" />
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="10" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<TextBox x:Name="InputText" Width="Auto" Height="20"
Text="{Binding Path=InfoText, ElementName=PromptBox, Mode=OneWay}"
Grid.Row="0" BorderThickness="0" Foreground="#FF8D8D8D"
GotFocus="InputText_GotFocus" LostFocus="InputText_LostFocus" />
<Separator Grid.Row="1" />
<Button Content="{Binding Path=ButtonText, ElementName=PromptBox}" Grid.Row="2"
Width="100" Command="{Binding Path=OkCommand, ElementName=PromptBox}" />
</Grid>
</Border>
What I want to do is this:
when the user clicks on the button, I'd like to run some code (obviously :) ) - this control will be used in some other controls / windows, and the code I'd like to run will be different depending on a scenarion. So how do I bind the Command property of this button with some custom command? Example usage:
<ctrls:Prompt Show="{Binding ShouldLogIn}" ButtonText="{Binding LogInText}"
InfoText="{Binding LogInInfo}" OkCommand="what goes here???" Grid.Row="0" Grid.ZIndex="2" />
Also - I follow the MVVM patern, using the MVVMLight fw, so I'd like the solution to follow it as well.
So the question is - How do I bind to the Button.Command from outside of the prompt control?
I would also suggest making a CustomControl, but if you want to use your UserControl you will need to add a DependencyProperty in your code behind.
public partial class Prompt : UserControl
{
private bool _canExecute;
private EventHandler _canExecuteChanged;
/// <summary>
/// DependencyProperty for the OKCommand property.
/// </summary>
public static readonly DependencyProperty OKCommandProperty = DependencyProperty.Register("OKCommand", typeof(ICommand), typeof(Prompt), new PropertyMetadata(OnOKCommandChanged));
/// <summary>
/// Gets or sets the command to invoke when the OKButton is pressed.
/// </summary>
public ICommand OKCommand
{
get { return (ICommand)GetValue(OKCommandProperty); }
set { SetValue(OKCommandProperty, value); }
}
/// <summary>
/// Gets a value that becomes the return value of
/// System.Windows.UIElement.IsEnabled in derived classes.
/// </summary>
protected override bool IsEnabledCore
{
get { return base.IsEnabledCore && _canExecute; }
}
// Command dependency property change callback.
private static void OnOKCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Prompt p = (Prompt)d;
p.HookUpCommand((ICommand)e.OldValue, (ICommand)e.NewValue);
}
public Prompt()
{
InitializeComponent();
}
// Add the command.
private void AddCommand(ICommand command)
{
EventHandler handler = new EventHandler(CanExecuteChanged);
_canExecuteChanged = handler;
if (command != null)
command.CanExecuteChanged += _canExecuteChanged;
}
private void CanExecuteChanged(object sender, EventArgs e)
{
if (OKCommand != null)
_canExecute = OKCommand.CanExecute(null);
CoerceValue(UIElement.IsEnabledProperty);
}
// Add a new command to the Command Property.
private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
{
// If oldCommand is not null, then we need to remove the handlers.
if (oldCommand != null)
RemoveCommand(oldCommand);
AddCommand(newCommand);
}
// Remove an old command from the Command Property.
private void RemoveCommand(ICommand command)
{
EventHandler handler = CanExecuteChanged;
command.CanExecuteChanged -= handler;
}
}
I have a grid in WPF that contains a button which should make a user control visible. How do I make this possible using MVVM pattern and /or code behind?
In your view model you want a bool property for the visibility of the user control. We'll call it IsUserControlVisible. Now you'll need a command in your view model that will set the IsUserControlVisible property to true. We'll call this ShowUserControlCommand.
In XAML you would bind the visibility of the User Control to IsUserControlVisible. In WPF there is a BooleanToVisibilityConverter, so we don't have to create our own converter. Your XAML would look something like this.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Command="{Binding ShowUserControlCommand}">Show</Button>
<UserControl Grid.Row="1" Visibility="{Binding IsUserControlVisible, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</Window>
I hope this helps.
Following a full example on how you can achieve this in MVVM with an illustration of ICommand interface.
your main should look like this
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525"
xmlns:my="clr-namespace:WpfApplication3">
<Grid>
<my:UserControl1 Background="Aqua"
Visibility="{Binding ChangeControlVisibility,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left"
Margin="111,66,0,0"
x:Name="userControl11"
VerticalAlignment="Top"
Height="156"
Width="195" />
<Button Content="Button"
Height="36"
HorizontalAlignment="Left"
Margin="36,18,0,0"
Name="button1"
VerticalAlignment="Top"
Width="53"
Command="{Binding MyButtonClickCommand}" />
</Grid>
</Window>
MainWindow.cs
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
}
}
ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
public MyViewModel()
{
_myCommand = new MyCommand(FuncToCall,FuncToEvaluate);
}
private ICommand _myCommand;
public ICommand MyButtonClickCommand
{
get { return _myCommand; }
set { _myCommand = value; }
}
private void FuncToCall(object context)
{
//this is called when the button is clicked
//for example
if (this.ChangeControlVisibility== Visibility.Collapsed)
{
this.ChangeControlVisibility = Visibility.Visible;
}
else
{
this.ChangeControlVisibility = Visibility.Collapsed;
}
}
private bool FuncToEvaluate(object context)
{
return true;
}
private Visibility _visibility = Visibility.Visible;
public Visibility ChangeControlVisibility
{
get { return _visibility; }
set {
_visibility = value;
this.OnPropertyChanged("ChangeControlVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Command:
class MyCommand : ICommand
{
public delegate void ICommandOnExecute(object parameter);
public delegate bool ICommandOnCanExecute(object parameter);
private ICommandOnExecute _execute;
private ICommandOnCanExecute _canExecute;
public bool CanExecute(object parameter)
{
return _canExecute.Invoke(parameter);
}
public MyCommand(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod)
{
_execute = onExecuteMethod;
_canExecute = onCanExecuteMethod;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
}
Generally speaking you have a boolean flag in your viewmodel that is bound to the user controls Visibility using an appropriate converter. You have a command in your viewmodel that is bound to the button's Command property. The Execute method of the command toggles the boolean flag.
Edit:
If you only need the button to make something visible on form, consider the Expander control that already does this out of the box.
<Expander>
<YourUserControl/>
</Expander>