I have ObservableCollection of DeviceInformation which is added in MainWindowViewModel and linked with DataContext.
public partial class MainWindow : Window
{
MainWindowViewModel viewModel = new MainWindowViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = viewModel;
}
}
Here is the MainWindowViewModel:
public class MainWindowViewModel : ViewModelBase
{
private ObservableCollection<DeviceInformation> allDeviceInfo = new ObservableCollection<DeviceInformation>();
public MainWindowViewModel()
{
// here some of the commands
}
public ObservableCollection<DeviceInformation> AllDeviceInfo
{
get { return allDeviceInfo; }
set
{
allDeviceInfo = value;
this.RaisePropertyChanged(nameof(AllDeviceInfo));
}
}
}
The RaisePropertyChanged is done with implementing ViewModelBase which looks like this:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
this.RaisePropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void RaisePropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
Inside my DeviceInformation I have a List of SyntaxMessages:
public class DeviceInformation : ViewModelBase
{
private List<SyntaxMessages> list = new List<SyntaxMessages>();
private string test = "";
public List<SyntaxMessages> ComConsoleMessages{
get { return list; } // get method
set
{
list = value;
RaisePropertyChanged(nameof(ComConsoleMessages));
} // set method
}
public string Test{
get { return test; } // get method
set
{
test = value;
RaisePropertyChanged(nameof(Test));
} // set method
}
}
This is how the SyntaxMessages looks:
public class SyntaxMessages : ViewModelBase
{
#region private values
private string message = "";
private string status = "";
private string color = "Transparent";
#endregion
#region Public values
public string Message {
get { return message; }
set
{
message = value;
RaisePropertyChanged(nameof(Message));
}
}
public string Status {
get { return status; }
set
{
status = value;
RaisePropertyChanged(nameof(Status));
}
}
public string Color {
get { return color; }
set
{
color = value;
RaisePropertyChanged(nameof(Color));
}
}
#endregion
}
So when I running my program and connecting device to it will collect and add information the the DeviceInformation and this will be added to ObervableCollection of DeviceInformation. This will update my MainTabControl by adding new tab and binding many strings like "Test" (there is more then one) to the TextBoxes, and also update the SubTabControl which is inside the main one. Inside both of the TabItems inside SubTabControl I also have a ListView to which I want link the List of SyntaxMessages this looks like this:
<ListView
Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="5"
MinHeight="40"
Padding="0"
Margin="2"
ItemsSource="{Binding ComConsoleMessages, UpdateSourceTrigger=PropertyChanged}">
<ListView.View>
<GridView
AllowsColumnReorder="False">
<GridViewColumn
DisplayMemberBinding="{Binding Message}"
Header="Message" />
<GridViewColumn
Header="Status">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Status}" Foreground="{Binding Color}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Problem
All works fine except a ListView. When I add some SyntaxMessages to the List of SyntaxMessages called ComConsoleMessages then I have to switch between SubTabControl tabs(SubTabItem1/2) to see ListView updated. I want to update ListView every single time when new message is added to the List of SyntaxMessages which is inside DeviceInfromation which is indside ObservableCollection of DeviceInfromations that is linked via MainWindowViewMode to the window DataContext.
Here is the view:
Related
In my view, I have a ListBox with some templated items that contain buttons.
<ListBox x:Name="MyListBox" ItemTemplate="{DynamicResource DataTemplate1}"
ItemsSource="{Binding MyItems}">
</ListBox>
And the template for generated items:
<DataTemplate x:Key="DataTemplate1">
<StackPanel Orientation="Horizontal">
<Button Width="50" Click="Button_Click" />
</StackPanel>
</DataTemplate>
When user clicks a button on one of those ListBox items, I want to send the index of that ListBox item to my ViewModel.
So figured to use Binding as it seems to be the way in MVVM. But I'm struggling to set up a binding in code between two properties.
My View code is as follows:
public partial class ItemView : UserControl
{
ViewModel.ItemViewModel VM;
public ItemView()
{
InitializeComponent();
VM = new ViewModel.ItemViewModel();
this.DataContext = VM;
}
private int clickedItemIndex;
public int ClickedItemIndex { get => clickedItemIndex; set => clickedItemIndex = value; }
private void Button_Click(object sender, RoutedEventArgs e)
{
var ClickedItem = (sender as FrameworkElement).DataContext;
ClickedItemIndex = MyListBox.Items.IndexOf(ClickedItem);
}
}
I get the index and set it to ClickedItemIndex property,
I also have property in my ViewModel:
public int SomeInt { get; set; }
Now how do I set up a binding between these two properties?
I'm quite new to MVVM and still learning it. So, maybe this not the correct approach. But I need to have a way for each individual listbox item to be able to call upon an effect in more global viewmodel. For example, if I wanted to have a "Remove" button on each of the listbox items, I would somehow need to send the index to the viewmodel and call the removeItem method with index as the parameter. Or is there a better way to do similar things?
I have a sample app created just for this scenario. I know it seems a lot of code at first glance. Copy this code in your project, that will help debug and get a hang of it(MVVM, databinding, commands and so on).
usercontrol.xaml
<UserControl x:Class="WpfApplication1.UserControl1"
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:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type local:Model}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=Name}"/>
<Button Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.UpdateCommand}"
CommandParameter="{Binding}"
Content="Update"/>
<Button Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.RemoveCommand}"
CommandParameter="{Binding}"
Content="Remove"/>
</StackPanel>
</DataTemplate>
</Grid.Resources>
<ListBox ItemsSource="{Binding Models}">
</ListBox>
</Grid>
usercontrol.cs
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
View model
public class ViewModel : INotifyPropertyChanged
{
private Models _Models;
public Models Models
{
get { return _Models; }
set { _Models = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Models)));
}
}
public ViewModel()
{
Models = new Models();
UpdateCommand = new Command(o => true, UpdateItem);
RemoveCommand = new Command(o => true, RemoveItem);
}
void RemoveItem(object item)
{
Model m = (item as Model);
Models.Remove(m);
}
void UpdateItem(object item)
{
Model m = (item as Model);
m.Name = m.Name + " updated";
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public ICommand UpdateCommand { get; private set; }
public ICommand RemoveCommand { get; private set; }
}
Icommand implementation
public class Command : ICommand
{
private readonly Func<object, bool> _canExe;
private readonly Action<object> _exe;
public Command(Func<object,bool> canExecute,Action<object> execute)
{
_canExe = canExecute;
_exe = execute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExe(parameter);
}
public void Execute(object parameter)
{
_exe(parameter);
}
}
Model and a collection of models
public class Models : ObservableCollection<Model>
{
public Models()
{
Add(new Model ());
Add(new Model ());
Add(new Model ());
Add(new Model ());
}
}
public class Model : INotifyPropertyChanged
{
static int count = 0;
public Model()
{
Name = "Model "+ ++count;
}
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
You don't need to use a Button in order to select the item. When you click/tap on the item it will get automatically selected.
Then simply bind ListBox.SelectedIndex to your view model property SomeInt and it will update on every selection.
Data binding overview in WPF
You can also get the item itself by binding ListBox.SelectedItem to your view model.
You can handle new values by invoking a handler from the property's set method:
ViewModel.cs
class ViewModel : INotifyPropertyChanged
{
private int currentItemIndex;
public int CurrentItemIndex
{
get => this.currentItemIndex;
set
{
this.currentItemIndex = value;
OnPropertyChanged();
// Handle property changes
OnCurrentItemIndexChanged();
}
}
private MyItem currentItem;
public MyItem CurrentItem
{
get => this.currentItem;
set
{
this.currentItem = value;
OnPropertyChanged();
}
}
protected virtual void OnCurrentItemIndexChanged()
{
// Handle the new this.CurrentItemIndex value
}
// Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ItemView .xaml
<UserControl>
<UserControl.DataContext>
<ViewModel />
</UserControl.DataContext>
<ListBox ItemsSource="{Binding MyItems}"
SelectedIndex="{Binding CurrentItemIndex}"
SelectedItem="{Binding CurrentItem}" />
</UserControl>
I have a WorkspaceViewModel that handles addition and deletion of tab items dynamically through an ObservableCollection. Each time a tab is connected to a PayslipModel, all bindings work fine but one problem I am having is that;
I have a save button in the UserControl who's DataContext is set to WorkspaceViewModel and I would like to save whatever info is being displayed in the selected tab. Now, each time a tab is added, a new instance of PayslipModel is created, which is exactly what I want because I don't want bindings to be shared for all tabs. However, I am unable to save what is being displayed since PayslipModel has multiple instances, therefore nothing is returned (temporarily using MessageBox to test if info is being retrieved) when I hit save.
I created a diagram to better explain my situation:
Is it possible to access the current instance when a tab is selected or cycle through all instances and do something like batch saving?
This is a working example which shows one of the possiblities:
View
<TabControl DataContext="{Binding}" ItemsSource="{Binding Models}" >
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" >
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DockPanel>
<Button DockPanel.Dock="Top" Content="Click Me" Command="{Binding DataContext.PCommand,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TabControl}}"
CommandParameter="{Binding Desc}"/>
<TextBlock Text="{Binding Desc}" >
</TextBlock>
</DockPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Model View
public class ModelView
{
public ModelView()
{
_models = new ObservableCollection<Model>();
_pCommand = new Command(DoParameterisedCommand);
}
ObservableCollection<Model> _models;
public ObservableCollection<Model> Models { get { return _models; } }
private void DoParameterisedCommand(object parameter)
{
MessageBox.Show("Parameterised Command; Parameter is '" +
parameter.ToString() + "'.");
}
Command _pCommand;
public Command PCommand
{
get { return _pCommand; }
}
}
Model
public class Model : INotifyPropertyChanged
{
string _desc;
public string Desc { get { return _desc; } set { _desc = value; RaisePropertyChanged("Desc"); } }
string _name;
public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
Command
public class Command : ICommand
{
public Command(Action<object> parameterizedAction, bool canExecute = true)
{
_parameterizedAction = parameterizedAction;
_canExecute = canExecute;
}
Action<object> _parameterizedAction = null;
bool _canExecute = false;
public bool CanExecute
{
get { return _canExecute; }
set
{
if (_canExecute != value)
{
_canExecute = value;
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}
public event EventHandler CanExecuteChanged;
bool ICommand.CanExecute(object parameter)
{
return _canExecute;
}
void ICommand.Execute(object parameter)
{
this.DoExecute(parameter);
}
public virtual void DoExecute(object param)
{ if (_parameterizedAction != null)
_parameterizedAction(param);
else
throw new Exception();
}
}
Use this to initialize:
public MainWindow()
{
InitializeComponent();
ModelView mv = new ModelView();
mv.Models.Add(new Model() { Name = "a", Desc = "aaa" });
mv.Models.Add(new Model() { Name = "b" , Desc = "bbb"});
mv.Models.Add(new Model() { Name = "c", Desc = "cccc" });
this.DataContext = mv;
}
I have a UI which displays a ListView in a ListView:
<ListView
SelectedIndex="{x:Bind ParentViewModel.SelectedParentIndex, Mode=TwoWay}"
ItemsSource="{x:Bind ParentViewModel.ParentViewModels, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewModels:ParentViewModel">
<StackPanel>
<TextBlock Text="{Binding ParentName}" />
<ListView
SelectedIndex="{x:Bind SelectedChildIndex, Mode=TwoWay}"
ItemsSource="{Binding ChildViewModels, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewModels:ChildViewModel">
<TextBlock Text="{Binding ChildName}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When I click on a parent element the SelectedParentIndex gets set and when I click on a child element the SelectedChildIndex gets set.
My problem is that when I click on a child element i don't know to wich parent element it belongs because the SelectedParentIndex is not set. How can I solve this?
And the flow how it should be:
Just add an event in. Here is a compiled working example.
<ListView
ItemsSource="{Binding ParentViewModels, Mode=OneWay}"
SelectedIndex="{Binding SelectedParentIndex, Mode=TwoWay}"
SelectedItem="{Binding SelectedParent,Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate >
<StackPanel>
<TextBlock Text="{Binding ParentName}" />
<ListView
ItemsSource="{Binding ChildViewModels, Mode=OneWay}"
SelectedIndex="{Binding SelectedChildIndex, Mode=TwoWay}"
SelectedItem="{Binding SelectedChild,Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ChildName}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here are the cs files. Please pay close attention to the structure.
The MasterViewModel is your DataContext for your View. It handles the SelectedParent, the SelectedParentIndex and your parents collection.
public class MasterViewModel : ViewModelBase
{
private ParentViewModel _SelectedParent;
public ParentViewModel SelectedParent
{
get { return _SelectedParent; }
set
{
_SelectedParent = value;
OnPropertyChanged("SelectedParent");
}
}
private int _SelectedParentIndex;
public int SelectedParentIndex
{
get { return _SelectedParentIndex; }
set
{
_SelectedParentIndex = value;
OnPropertyChanged("SelectedParentIndex");
}
}
public ObservableCollection<ParentViewModel> ParentViewModels
{
get; private set;
}
public MasterViewModel()
{
ParentViewModels = new ObservableCollection<ParentViewModel>();
LoadData();
}
private void LoadData()
{
for(int x = 0; x < 10; x++)
{
ParentViewModel parent = new ParentViewModel();
parent.ChildChangedEvent += Parent_ChildChangedEvent;
for(int y = 0; y < 20; y++)
{
ChildViewModel child = new ChildViewModel()
{ ChildName = "Child " + y };
parent.ChildViewModels.Add(child);
}
ParentViewModels.Add(parent);
}
}
private void Parent_ChildChangedEvent(object sender, EventArgs e)
{
SelectedParent = (ParentViewModel)sender;
}
}
Your ParentViewModel contains your SelectedChildIndex, your SelectedChild and your ChildViewModels collection. It also has a name property
Notice that I added an EventHandler to your ParentViewModel. When the SelectedChild is updated, it fires the event off. Then, we handle this event in the MasterViewModel where we can force the SelectedParent to update.
public class ParentViewModel : ViewModelBase
{
public String ParentName { get; set; }
private int _SelectedChildIndex;
public int SelectedChildIndex
{
get { return _SelectedChildIndex; }
set
{
_SelectedChildIndex = value;
OnPropertyChanged("SelectedChildIndex");
}
}
private ChildViewModel _SelectedChild;
public ChildViewModel SelectedChild
{
get { return _SelectedChild; }
set
{
_SelectedChild = value;
OnPropertyChanged("SelectedChild");
if (ChildChangedEvent != null)
{
ChildChangedEvent(this, new EventArgs());
}
}
}
public ObservableCollection<ChildViewModel> ChildViewModels
{
get; private set;
}
public event EventHandler ChildChangedEvent;
public ParentViewModel()
{
ChildViewModels = new ObservableCollection<ChildViewModel>();
}
}
Your ChildViewModel just has a name property.
public class ChildViewModel : ViewModelBase
{
private string _childName;
public string ChildName
{
get { return _childName; }
set
{
_childName = value;
OnPropertyChanged("ChildName");
}
}
}
The ViewModelBase just updates the UI
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propName));
}
}
}
I am unable figure out why the databinding is not working as expected:
I created a Listbox and set its ItemSource to my observable collection
I used this.DataContext = this
I Initialized my public Observable Collection
I filled it with objects that implement INotifyPropertyChanged
Yet, the databinding, still does not work. My Listbox:
<ListBox Height="425" ItemsSource="{Binding headers}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=HeaderInfo}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The code behind:
public partial class cornet_controls : PhoneApplicationPage
{
public ObservableCollection<headerInfo> headers;
public cornet_controls()
{
InitializeComponent();
this.DataContext = this;
headers = new ObservableCollection<headerInfo>();
for (int x = 0; x < 100; x++)
headers.Add((new headerInfo() { HeaderInfo = x.ToString() }));
}
}
My custom class implementing INotifyPropertyChanged:
public class headerInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public headerInfo()
{}
private String _HeaderInfo;
public String HeaderInfo
{
get { return _HeaderInfo; }
set { _HeaderInfo = value; NotifyPropertyChanged("HeaderInfo"); }
}
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You cannot bind to a NonProperty:
<ListBox Height="425" ItemsSource="{Binding headers}">
public ObservableCollection<headerInfo> headers;
you need to bind to a Property like:
public ObservableCollection<headerInfo> headers { get; set; }
In WPF, I have a ListView of 2 columns and the first column needs to be a button. Correct me if I'm wrong, but the only way I found to implement a button in a ListView is to use a DataTemplate. The problem I found with this is I have no way to maintain my original button Properties when they are mapped with a DataTemplate so I am forced to use binding to remap every individual property (including custom Properties since I'm actually using a custom User Control which inherits from Button). This seems extraneous to have to manually map all Properties so maybe there's a better way to automatically persist those properties?
Here's my test code:
public MainWindow() {
InitializeComponent();
ObservableCollection<ScreenRequest> screenRequests = new ObservableCollection<ScreenRequest>() {
new ScreenRequest("A", "1"),
new ScreenRequest("B", "2")
};
myListView.ItemsSource = screenRequests;
}
public class ScreenRequest {
public CustomButton ScreenButton { set; get; }
public string Details { set; get; }
public ScreenRequest(string buttonText, string customProperty) {
this.ScreenButton = new CustomButton();
this.ScreenButton.Content = buttonText;
this.ScreenButton.CustomProperty = customProperty;
this.ScreenButton.Click += new RoutedEventHandler(InitiateScreenRequest);
}
private void InitiateScreenRequest(object sender, RoutedEventArgs e) {
CustomButton screenBtn = (CustomButton)sender;
screenBtn.Content = "BUTTON TEXT CHANGED";
}
}
public class CustomButton : Button {
public string CustomProperty { get; set; }
}
And the XAML:
<Window...
...
<Window.Resources>
<DataTemplate x:Key="ButtonTemplate">
<local:CustomButton Content="{Binding ScreenButton.Content}"/>
</DataTemplate>
</Window.Resources>
<Grid x:Name="grdMain">
...
<ListView...
<ListView.View>
<GridView x:Name="gridView">
<GridViewColumn CellTemplate="{StaticResource ButtonTemplate}" Width="Auto" Header="Screen" HeaderStringFormat="Screen"/>
<GridViewColumn Header="Details" HeaderStringFormat="Details" DisplayMemberBinding="{Binding Details}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
So my questions are:
Do I have to manually map every single property in the CustomButton in order for it to carry over to the DataTemplate or is their a catch-all to automatically persist the Properties?
How do I map the CustomProperty Property in the binding such that it sticks with the button? Do I use a DependencyProperty for this?
How do I maintain my click event such that clicking the button in GridView will call the InitiateScreenRequest function? Ideally I'd like to have a single method declared for all buttons, but I haven't gotten to that point yet.
Any help or insight into buttons in listviews would be appreciated.
<Window x:Class="MiscSamples.TonyRush"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TonyRush" Height="300" Width="300">
<ListView ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto" Header="Screen" HeaderStringFormat="Screen">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding SomeAction}" Content="{Binding ActionDescription}" Width="100"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Details" HeaderStringFormat="Details" DisplayMemberBinding="{Binding Details}" Width="100"/>
</GridView>
</ListView.View>
</ListView>
</Window>
Code Behind:
public partial class TonyRush : Window
{
public TonyRush()
{
InitializeComponent();
DataContext = new List<ScreenRequest>
{
new ScreenRequest() {ActionDescription = "Click Me!"},
new ScreenRequest() {ActionDescription = "Click Me Too!"},
new ScreenRequest() {ActionDescription = "Click Me Again!!"},
};
}
}
ViewModel:
public class ScreenRequest: INotifyPropertyChanged
{
public Command SomeAction { get; set; }
private string _actionDescription;
public string ActionDescription
{
get { return _actionDescription; }
set
{
_actionDescription = value;
NotifyPropertyChanged("ActionDescription");
}
}
private string _details;
public string Details
{
get { return _details; }
set
{
_details = value;
NotifyPropertyChanged("Details");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ScreenRequest()
{
SomeAction = new Command(ExecuteSomeAction) {IsEnabled = true};
}
//public SomeProperty YourProperty { get; set; }
private void ExecuteSomeAction()
{
//Place your custom logic here based on YourProperty
ActionDescription = "Clicked!!";
Details = "Some Details";
}
}
Key part: The Command class:
//Dead-simple implementation of ICommand
//Serves as an abstraction of Actions performed by the user via interaction with the UI (for instance, Button Click)
public class Command : ICommand
{
public Action Action { get; set; }
public void Execute(object parameter)
{
if (Action != null)
Action();
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action action)
{
Action = action;
}
}
Result:
Notes:
Take a look at how separate UI is from Data and functionality. This is the WPF way. Never mix UI with data / business code.
The Command in the ViewModel serves as an abstraction for the Button. The ViewModel doesn't know what a Button is, nor should it. Let me know if you need further details.