WPF Binding to behavior property is not working - c#

I have a custom behavior that take a boolean as parameter. I'm trying to bind it to a property in my model but I can't get it to work. (I'm using DevExpress)
<dxg:GridControl
x:Name="dgArticles"
Grid.Row="1"
AutoGenerateColumns="None"
ItemsSource="{Binding Path=Articles, Mode=TwoWay}">
<dxmvvm:Interaction.Behaviors>
<util:ExpandAllBehavior HasLotPartie="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CahierDesCharges.HasLotPartie }" />
</dxmvvm:Interaction.Behaviors>
</dxg:GridControl
This is a WPF UserControl and the DataContext is defined in code-behind as follow:
public partial class GridView : UserControl
{
public GridView(ArticleViewModel a)
{
InitializeComponent();
this.DataContext = a;
}
}
My ViewModel:
public class ArticleViewModel : INotifyPropertyChanged
{
private Cahier cahierDesCharges;
public Cahier CahierDesCharges { get { return cahierDesCharges; } set { } }
private ObservableCollection<Article> articles;
public ObservableCollection<Article> Articles
{
get { return articles; }
set { articles = value; OnPropertyChanged("articles"); }
}
public ArticleViewModel() { }
public ArticleViewModel(Cahier c)
{
this.cahierDesCharges = c;
this.articles = CahierDesCharges.Articles;
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
And the CahierDesCharges Class:
public class Cahier : INotifyPropertyChanged
{
public bool HasLotPartie { get; set; }
private ObservableCollection<Article> articles;
public ObservableCollection<Article> Articles
{
get { return articles; }
set { articles = value; OnPropertyChanged("articles"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public Cahier() { }
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new System.ComponentModel.PropertyChangedEventArgs(propertyName));
Console.WriteLine(propertyName);
}
}
The Behavior:
public class ExpandAllBehavior : Behavior<GridControl>
{
public static readonly DependencyProperty HasLotPartieProperty =
DependencyProperty.Register("HasLotPartie", typeof(Boolean), typeof(ExpandAllBehavior));
public bool HasLotPartie
{
get { return (bool)GetValue(HasLotPartieProperty); }
set { SetValue(HasLotPartieProperty, value); }
}
protected override void OnAttached()
{
if (HasLotPartie)
{
base.OnAttached();
this.AssociatedObject.CustomRowFilter += AssociatedObject_Loaded;
}
}
void AssociatedObject_Loaded(object sender, EventArgs e)
{
int dataRowCount = AssociatedObject.VisibleRowCount;
for (int rowHandle = 0; rowHandle < dataRowCount; rowHandle++)
AssociatedObject.ExpandMasterRow(rowHandle);
}
protected override void OnDetaching()
{
base.OnDetaching();
}
}
I have used the line "{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CahierDesCharges.HasLotPartie }" at a different place and I know it's working. In the following example it's working perfectly fine:
<dxg:GridControl.View>
<dxg:TableView
AllowScrollAnimation="True"
EnableImmediatePosting="True"
IsDetailButtonVisibleBinding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.CahierDesCharges.HasLotPartie }"
Name="view"
ShowGroupedColumns="False"
ShowGroupPanel="False"
UseLightweightTemplates="None" />
</dxg:GridControl.View>
The behavior works fine if I manually set the value to true: <util:ExpandAllBehavior HasLotPartie="True" /> So I thought that it might be that the DadaContext is not inherited and I use the trick explain by Thomas Levesque on his blog here and this is what I got:
<dxg:GridControl.Resources>
<util:BindingProxy x:Key="proxy" Data="{Binding}" />
</dxg:GridControl.Resources>
<dxmvvm:Interaction.Behaviors>
<util:ExpandAllBehavior HasLotPartie="{Binding Data.CahierDesCharges.HasLotPartie, Source={StaticResource proxy}}" />
</dxmvvm:Interaction.Behaviors>
I've used this trick several time and I know it works too but not in this scenario. Right now I'm stuck with this after several hours of searching so any help is very welcome.
Thank you

Ok so I figured that my CahierDesCharges was null when the behavior was called. Therefore HasLotPartie was false no matter what and so it did not reach my event. So I just moved my if() in AssociatedObject_Loaded method and it worked. I needed to wait until the GridControl was fully loaded so the CahierDesCharges wasn't null anymore.
This is my behavior class now:
public class ExpandAllBehavior : Behavior<GridControl>
{
public static readonly DependencyProperty HasLotPartieProperty =
DependencyProperty.Register("HasLotPartie", typeof(bool), typeof(ExpandAllBehavior));
public bool HasLotPartie
{
get { return (bool)GetValue(HasLotPartieProperty); }
set { SetValue(HasLotPartieProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Loaded += AssociatedObject_Loaded; //Apllied after GridControl is fully loaded.
this.AssociatedObject.CustomRowFilter += AssociatedObject_Loaded; //Applied after filtering on Rows
}
void AssociatedObject_Loaded(object sender, EventArgs e)
{
if (HasLotPartie)
{
int dataRowCount = AssociatedObject.VisibleRowCount;
for (int rowHandle = 0; rowHandle < dataRowCount; rowHandle++)
AssociatedObject.ExpandMasterRow(rowHandle);
}
}
protected override void OnDetaching()
{
base.OnDetaching();
}
}
Now my MasterDetails are always expanded when HasLotPartie is true which was the wanted effect.
As always I was stuck focussing on complicated stuff while the solution was easy.

Related

PropertyChanged is null wpf

Help me please!!!
I have 3 UserControls
I select user on List Users UC listbox
Then send message from SendMessage UC to Database
when i send message to Db it must refresh my chat listBox in Correspondence UC, but problem is in my ChatWrapper.
PropertyChanged in ChatWrapper is always null, and I can't refresh my ListBox in Correspondence UC with new message
List Users:
public IEnumerable<EmployeesDb> getListNames
{
get { return Db.Instance.EmployeesDbs.ToList(); }
}
static EmployeesDb m_selectedUser;
public static EmployeesDb selectedUser
{
get { return m_selectedUser; }
set
{
if (value != null)
m_selectedUser = value;
Correspondence correspondence = new Correspondence();
correspondence.CorrespondenceChat();
}
}
}
Send Message ( I try to refresh -> SendInfo.FirstOrDefault().RefreshGUI();)
public static DependencyProperty SendInfoProperty =
DependencyProperty.Register(
"SendInfo",
typeof(IEnumerable<ChatWrapper>),
typeof(SendMessage));
public IEnumerable<ChatWrapper> SendInfo
{
get { return GetValue(SendInfoProperty) as IEnumerable<ChatWrapper>; }
set { SetValue(SendInfoProperty, value); }
}
void SendMessageCommandExecute()
{
//...
SendInfo.FirstOrDefault().RefreshGUI();
//...
}
ChatWrapper
public class ChatWrapper : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void FirePropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
public void RefreshGUI()
{
FirePropertyChanged("message");
}
public ChatDb chatDb { get; set; }
public string message
{
get
{
return (chatDb != null) ? string.Format("{0} {1}.{2} / {3} / {4}\n{5}",
chatDb.FromEmployeesDb.surname,
chatDb.FromEmployeesDb.name[0],
chatDb.FromEmployeesDb.middleName[0],
chatDb.messageDateTime,
chatDb.computerName,
chatDb.message) : null;
}
}
Correspondence
//...
public partial class Correspondence : UserControl, INotifyPropertyChanged
{
public static DependencyProperty GetCorrespondenceInfoProperty =
DependencyProperty.Register(
"GetCorrespondenceInfo",
typeof(IEnumerable<ChatWrapper>),
typeof(Correspondence),
new PropertyMetadata(OnChanged));
public IEnumerable<ChatWrapper> GetCorrespondenceInfo
{
get { return GetValue(GetCorrespondenceInfoProperty) as IEnumerable<ChatWrapper>; }
set { SetValue(GetCorrespondenceInfoProperty, value); }
}
static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var me = d as Correspondence;
me.chat = me.GetCorrespondenceInfo;
}
ICollectionView m_CollectionView;
public static IEnumerable<ChatWrapper> m_chat;
public IEnumerable<ChatWrapper> chat
{
get { return m_chat; }
set
{
m_chat = value;
if (ListUsers.selectedUser != null)
CorrespondenceChat();
FirePropertyChanged("chat");
}
}
public void CorrespondenceChat()
{
if (m_chat == null)
return;
m_CollectionView = CollectionViewSource.GetDefaultView(m_chat);
//...
FirePropertyChanged("chat");
}
XAML of Correspondence (refresh
<Grid>
<ListBox x:Name="correspondenceListBox" ItemsSource="{Binding chat, RelativeSource={RelativeSource AncestorType={x:Type local:Correspondence}}}"
Height="auto" Grid.Row="0" Grid.Column="1" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding message}" TextWrapping="Wrap" Width="auto"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I tried to write
public event PropertyChangedEventHandler PropertyChanged = delegate { };
PropertyChanged is no longer null, but it's still not updated

Master View Details with MVVM Pattern

I have a MVVM - WPF Browser Application application using Entity Framework 6.
I have tried to select a supplierDataGrid row and display the Products related in another datagrid. But it doesn't work. The application freezes when I select a row. Thanks for your help!
ViewModelBase
public class CommandBase<T> : INotifyPropertyChanged
{
#region "INotifyPropertyChanged members"
public event PropertyChangedEventHandler PropertyChanged;
//This routine is called each time a property value has been set.
//This will //cause an event to notify WPF via data-binding that a change has occurred.
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private ObservableCollection<T> collection;
public ObservableCollection<T> Collection
{
get
{
if (collection == null)
{
Get();
}
return collection;
}
set { collection = value;
OnPropertyChanged("Collection"); }
}
private T _selected;
public T Selected
{
get {
if(_selected != null)
GetSub();
return _selected;
}
set { _selected = value;
OnPropertyChanged("Selected"); }
}
private ICommand getCommand;
private ICommand saveCommand;
private ICommand removeCommand;
private ICommand getSubCommand;
protected virtual void Save()
{
//return true;
}
protected virtual bool CanSave()
{
return true;
}
public ICommand DeleteCommand
{
get
{
// return removeCommand ?? (removeCommand = new RelayCommand(Delete,CanDelete));
return removeCommand ?? (removeCommand = new RelayCommand(p => this.Delete(), p => this.CanDelete()));
}
}
protected virtual void Delete()
{ }
protected virtual bool CanDelete()
{
if (Selected != null)
return true;
else
return false;
}
public ICommand GetSubCommand
{
get
{
return getSubCommand ?? (getSubCommand = new RelayCommand(p => this.GetSub(), p => this.CanGetSub()));
}
}
protected virtual async Task GetSub()
{
await Task.Delay(0);
}
protected virtual bool CanGetSub()
{
return true;
}
}
ViewModel
public class SupplierViewModel : CommandBase<foodSupplier>
{
public Context ctx = new Context();
protected override void Get()
{
ctx.foodSuppliers.ToList().ForEach(supplier => ctx.foodSuppliers.Local.Add(supplier));
Collection = ctx.foodSuppliers.Local;
}
protected override bool CanGet()
{
return true;
}
protected override void Save()
{
foreach (foodSupplier item in Collection)
{
if (ctx.Entry(item).State == System.Data.Entity.EntityState.Added)
{
ctx.foodSuppliers.Add(item);
}
}
ctx.SaveChanges();
}
protected override void Delete()
{
var id = Selected;
var supp = (from s in ctx.foodSuppliers
where s.idfoodSupplier == id.idfoodSupplier
select s).SingleOrDefault();
ctx.foodSuppliers.Remove(supp);
ctx.SaveChanges();
Collection.Remove(supp);
}
protected virtual bool CanDelete()
{
return true;
}
protected override async Task GetSub()
{
var supplier = Selected;
var pro = await (from p in ctx.products
where p.supplier == supplier.idfoodSupplier
select p).ToListAsync();
Products = pro;
}
private IList<product> _products;
public IList<product> Products
{
get
{
return _products;
}
set
{
_products = value;
OnPropertyChanged("Products");
}
}
}
View
<DataGrid x:Name="dataGrid"
Margin="5"
ItemsSource="{Binding Collection}"
AutoGenerateColumns="False"
SelectedItem="{Binding Selected, Mode=TwoWay}"
SelectionMode="Extended"
SelectionUnit="FullRow">
<DataGrid.Columns>
<DataGridTextColumn x:Name="dataGridTextColumn"
Header="Supplier"
Binding="{Binding idfoodSupplier, UpdateSourceTrigger=PropertyChanged}"
Visibility="Hidden" />
<DataGridTextColumn Header="Supplier"
Binding="{Binding supplier, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding GetCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
<Button Height="Auto"
Width="Auto"
Content="Delete"
Command="{Binding DeleteCommand}" />
EDIT: the program freezes, because you have infinite recursion. Selected getter call GetSub, it calls getter of Selected it call GetSub, etc...
I recommend you to call GetSub in Selected setter, but some additional refactoring may be needed.
TIP: when somethig freezes, pause the program in visual studio and take a look at callstack window. You would immediatelly know whats going on in this case. Maybe you will need to switch to Main thread in Threads window (Ctrl+Alt+H) after you press pause button.

Why Browsable(false) does not hide columns in DataGrid

In my WPF apllication I want to hide column in DataGrid with binding ItemsSource by adding [Browsable(false)] to some properties
But, with or without Browsable(false) all columns are visible.
My model:
public class Room : INotifyPropertyChanged
{
private int id;
...
[Browsable(false)]
public int Id
{
get
{
return this.id;
}
set
{
this.id = value;
this.OnPropertyChanged("Id");
}
}
...
public Room()
{
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChangedEventHandler = this.PropertyChanged;
if (propertyChangedEventHandler != null)
{
propertyChangedEventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
View:
<DataGrid Grid.Row="1" ItemsSource="{Binding Rooms}" SelectedItem="{Binding SelectedRoom, Mode=TwoWay}" />
How can I use Browsable(false) to hide columns?
You can hide them by hooking up the AutoGeneratingColumn Event on your DataGrid (see https://stackoverflow.com/a/3794324/4735446).
public class DataGridHideBrowsableFalseBehavior : Behavior<DataGrid>
{
protected override void OnAttached()
{
AssociatedObject.AutoGeneratingColumn += AssociatedObject_AutoGeneratingColumn;
base.OnAttached();
}
private void AssociatedObject_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
if (((PropertyDescriptor)e.PropertyDescriptor).IsBrowsable == false)
e.Cancel = true;
}
}
<DataGrid
ItemsSource="{Binding Path=DataGridSource, Mode=OneWay}"
AutoGenerateColumns="true">
<i:Interaction.Behaviors>
<behaviors:DataGridHideBrowsableFalseBehavior>
</behaviors:DataGridHideBrowsableFalseBehavior>
</i:Interaction.Behaviors>
</DataGrid>
You can't, I'm afraid. The Browsable attribute only affects the Properties view of the visual designer, it has NO effect during runtime...
For more info, check the MSDN page about BrowsableAttribute.
Don't call this.OnPropertyChanged("Id"); in your code.

How to select an item in LongListSelector using the MVVM-pattern?

I'm building application using the MVVM pattern. After clicking on one of the elements I want to see this element's details. I wrote this:
XAML
<phone:LongListSelector ItemsSource="{Binding Data}"
Margin="0,0,0,158"
SelectedItem="{Binding SelectedItem}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button>
<!-- Command="{Binding ShowDetailsAction}"-->
<Button.Template>
<ControlTemplate>
<TextBlock Text="{Binding Text}"></TextBlock>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
ViewModel:
public IEnumerable SelectedItem
{
get { return _itemsControl; }
set
{
if (_itemsControl == value)
return;
_itemsControl = value;
// Test
_mss.ErrorNotification("fd");
}
}
I tried also using a command, which didn't work, too.
This was the command part:
public ICommand ShowDetailsCommand { get; private set; }
public ViewModel()
{
_loadDataCommand = new DelegateCommand(LoadDataAction);
SaveChangesCommand = new DelegateCommand(SaveChangesAction);
ShowDetailsCommand = new DelegateCommand(ShowDetailsAction);
}
private void ShowDetailsAction(object p)
{
_mss.ErrorNotification("bla bla");
}
EDIT
ViewModel
private IEnumerable _itemsControl;
public IEnumerable Data
{
get
{
return _itemsControl;
}
set
{
_itemsControl = value;
RaisePropertyChanged("Data");
}
}
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Model
public string Text { get; set; }
public DateTimeOffset Data { get; set; }
EDIT2
private MobileServiceCollection<ModelAzure, ModelAzure> _items;
private readonly IMobileServiceTable<ModelAzure> _todoTable = App.MobileService.GetTable<ModelAzure>();
private async void RefreshTodoItems()
{
try
{
_items = await _todoTable.ToCollectionAsync();
}
catch (MobileServiceInvalidOperationException e)
{
_mss.ErrorNotification(e.ToString());
}
Data = _items;
}
Your Data property looks like
private MobileServiceCollection<ModelAzure, ModelAzure> _itemsControl;
public MobileServiceCollection<ModelAzure, ModelAzure> Data
{
get
{
return _itemsControl;
}
set
{
_itemsControl = value;
RaisePropertyChanged("Data");
}
}
Edited
It seems the SelectedItem property from LongListSelector cannot be bound in WP8.
What you can do is either :
Use the derived and fixed custom LongListSelector provided in the link above instead of the default one, which looks like :
public class LongListSelector : Microsoft.Phone.Controls.LongListSelector
{
public LongListSelector()
{
SelectionChanged += LongListSelector_SelectionChanged;
}
void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedItem = base.SelectedItem;
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
"SelectedItem",
typeof(object),
typeof(LongListSelector),
new PropertyMetadata(null, OnSelectedItemChanged)
);
private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var selector = (LongListSelector)d;
selector.SelectedItem = e.NewValue;
}
public new object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
}
Register the SelectionChanged event from LongListSelector and call your ViewModel by yourself inside the associated handler/callback :
in your view :
<phone:LongListSelector x:Name="YourLongListSelectorName"
ItemsSource="{Binding Data}"
Margin="0,0,0,158"
SelectionChanged="OnSelectedItemChanged">
in your code behind :
private void OnSelectedItemChanged(object sender, SelectionChangedEventArgs selectionChangedEventArgs e)
{
((YourViewModel)this.DataContext).NewSelectedItemMethodOrWhateverYouWant((ModelAzure)this.YourLongListSelectorName.SelectedItem);
//or
((YourViewModel)this.DataContext).SelectedItem = (ModelAzure)this.YourLongListSelectorName.SelectedItem;
}
Finally your Button command wasn't properly working, because when you use a DataTemplate, the ambiant DataContext is the item itself. Which means that it was looking for your Command into your Model instance, not into your ViewModel instance.
Hope this helps
In your ViewModel, you have:
public IEnumerable SelectedItem
{
get { return _itemsControl; }
set
{
if (_itemsControl == value)
return;
_itemsControl = value;
// Test
_mss.ErrorNotification("fd");
}
}
Why is your SelectItem an IEnumerable? Should it not be of type "Model"? Your list is bound to "Data" which should be ObservableList, not IEnumerable. It will provide it's own change notification, so you don't need to.
The list will set the SelectedItem when it gets selected, but if the type is wrong, it won't get set.
Greg

How can I handle multiple CheckBoxes in the MVVM pattern?

Binding checkbox in WPF is common issue, but I am still not finding example code which is easy to follow for beginners. I have check box list in WPF to select favorite sports’ name. The number of checkboxes is static in my case. Can anyone show me how to implement ViewModel for this issue?
FavoriteSportsView.xaml:
<StackPanel Height="50" HorizontalAlignment="Left" VerticalAlignment="Top"
Width="150">
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}"
Command="{Binding Path=SportsResponseCommand}"
CommandParameter="Football"
Content="Football"
Margin="5" />
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}"
Command="{Binding Path=SportsResponseCommand}"
CommandParameter="Hockey"
Content="Hockey"
Margin="5" />
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}"
Command="{Binding Path=SportsResponseCommand}"
CommandParameter="Golf"
Content="Golf"
Margin="5" />
</StackPanel>
FavoriteSportsViewModel.cs
public class FavoriteSportsViewModel.cs {
//Since I am using the same IsChecked in all check box options, I found all check
//boxes gets either checked or unchecked when I just check or uncheck one option.
//How do i resolve this issue? I don't think i need seprate IsChecked for each
//check box option.
private bool _isChecked;
public bool IsChecked{
get {
return _isChecked;
}
set { if (value != _isChecked)
_isChecked = value;
this.OnPropertyChanged("IsChecked");
}
}
//How do i detect parameter in this method?
private ICommand _sportsResponseCommand;
public ICommand SportsResponseCommand
{
get
{
if (_sportsResponseCommand== null)
_sportsResponseCommand= new
RelayCommand(a => DoCollectSelectedGames(), p => true);
return _sportsResponseCommand;
}
set
{
_sportsResponseCommand= value;
}
}
private void DoCollectSelectedGames(){
//Here i push all selected games in an array
}
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I'm not sure how to do the following in above ViewModel:
1. How do I implement single method to handle all my options?
2. how do I detect each one of the checkboxes to see whether checked or not
3. How do i utlize CommandParameter?
4. How do i implement SportsResponseCommand correctly
Your view model should look something like this:
public class MyViewModel : INotifyPropertyChanged
{
//INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
//bindable property
private bool _football;
public bool Football
{
get { return _football; }
set
{
if (value != _football)
{
_football = value;
this.OnPropertyChanged("Football");
}
}
}
//... and the same for Golf and Hockey
}
Then you associate your view model with the view by setting the DataContext property (this will most likely be in the Window or UserControl code behind, though there are a lot of ways to achieve this).
Finally, update your bindings so that they look like:
<CheckBox IsChecked="{Binding Football, Mode=TwoWay}"
Content="Football"
Margin="5" />
<CheckBox IsChecked="{Binding Golf, Mode=TwoWay}"
Content="Football"
Margin="5" />
As a final comment, you shouldn't really need to bind the Command property - you can just write whatever code you need to run in the property setter on the view model.
I highly recommend you to read this http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
I describe a solution below I tried to not modify your XAML code but it is not the only way (or the best approach) but contains all necessary elements!
At first step you need your model I call it Model_Sport
public class Model_Sport : INotifyPropertyChanged
{
#region Constructor
public Model_Sport(string name, ICommand command)
{
Name = name;
SportsResponseCommand = command;
}
#endregion
static readonly PropertyChangedEventArgs _NameEventArgs = new PropertyChangedEventArgs("Name");
private string _Name = null;
public string Name
{
get { return _Name; }
set
{
_Name = value;
OnPropertyChanged(_NameEventArgs);
}
}
static readonly PropertyChangedEventArgs _SportsResponseCommandEventArgs = new PropertyChangedEventArgs("SportsResponseCommand");
private ICommand _SportsResponseCommand = null;
public ICommand SportsResponseCommand
{
get { return _SportsResponseCommand; }
set
{
_SportsResponseCommand = value;
OnPropertyChanged(_SportsResponseCommandEventArgs);
}
}
static readonly PropertyChangedEventArgs _IsCheckedEventArgs = new PropertyChangedEventArgs("IsChecked");
private bool _IsChecked = false;
public bool IsChecked
{
get { return _IsChecked; }
set
{
_IsChecked = value;
OnPropertyChanged(_IsCheckedEventArgs);
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
{
if (PropertyChanged != null)
{
PropertyChanged(this, eventArgs);
}
}
#endregion
}
Now you need a way to delegate your command “SportsResponseCommand”, DelegateCommand object will help you to do that
public class DelegateCommand : ICommand
{
private readonly Action<object> _ExecuteMethod;
private readonly Func< object, bool> _CanExecuteMethod;
#region Constructors
public DelegateCommand(Action<object>executeMethod, Func<object, bool> canExecuteMethod)
{
if (null == executeMethod)
{
throw new ArgumentNullException("executeMethod", "Delegate Command Delegates Cannot Be Null");
}
_ExecuteMethod = executeMethod;
_CanExecuteMethod = canExecuteMethod;
}
public DelegateCommand(Action<object>executeMethod) : this(executeMethod, null) { }
#endregion
#region Methods
public bool CanExecute(object parameter)
{
if (_CanExecuteMethod == null) return true;
return _CanExecuteMethod(parameter);
}
public void Execute(object parameter)
{
if (_ExecuteMethod == null) return;
_ExecuteMethod(parameter);
}
bool ICommand.CanExecute(object parameter)
{
return CanExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
void ICommand.Execute(object parameter)
{
Execute(parameter);
}
#endregion
}
Now “ViewModel”
public class ViewModel
{
#region property
public Dictionary<string, Model_Sport> Sports { get; set; }
public DelegateCommand SportsResponseCommand { get; set; }
#endregion
public ViewModel()
{
Sports = new Dictionary<string, Model_Sport>();
SportsResponseCommand = new DelegateCommand(p => execute_SportsResponseCommand(p));
buildSports();
}
private void buildSports()
{
Model_Sport football = new Model_Sport("Football", SportsResponseCommand);
Model_Sport golf = new Model_Sport("Golf", SportsResponseCommand);
Model_Sport hockey = new Model_Sport("Hockey", SportsResponseCommand);
football.IsChecked = true; // just for test
Sports.Add(football.Name, football);
Sports.Add(golf.Name, golf);
Sports.Add(hockey.Name, hockey);
}
private void execute_SportsResponseCommand(object p)
{
// TODO :what ever you want
MessageBox.Show(p.ToString());
}
}
Now View
Remember to set datacontext for your Window
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
Then in XAML
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" >
<CheckBox DataContext="{Binding Path=Sports[Football]}"
IsChecked="{Binding IsChecked, Mode=TwoWay}"
Command="{Binding Path=SportsResponseCommand}"
CommandParameter="Football"
Content="Football"
Margin="5" />
<CheckBox DataContext="{Binding Path=Sports[Hockey]}"
IsChecked="{Binding IsChecked, Mode=TwoWay}"
Command="{Binding Path=SportsResponseCommand}"
CommandParameter="Hockey"
Content="Hockey"
Margin="5" />
<CheckBox DataContext="{Binding Path=Sports[Golf]}" IsChecked="{Binding IsChecked, Mode=TwoWay}"
Command="{Binding Path=SportsResponseCommand}"
CommandParameter="Golf"
Content="Golf"
Margin="5" />
</StackPanel>
If you just want a property in your ViewModel to get updated when the IsChecked changes, replace the Binding for IsChecked to a boolean property in your ViewModel that raises NotifyPropertyChanged on its "set".
Now if you want to perform an action everytime IsChecked changes for one of the 3 CheckBoxes:
First of all, replace your CommandParameter with "{Binding RelativeSource={RelativeSource Mode=Self}}"
In your ViewModel (that should implement INotifyPropertyChanged), create an ICommand (SportsResponseCommand) that takes a CheckBox in parameter.
In the command's method, check for the Content of your CheckBox, and for the "IsChecked" property then do your stuff with them.
If you have further questions let me know.
You can assign a view model by using this
//for the view
partial class MainView:Window
{
InitializeComponent();
this.DataContext=new MainViewModel();
}
//ViewModel Code
public class MainViewModel: INotifyPropertyChanged
{
//INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
//bindable property
private bool _football;
public bool Football
{
get { return _football; }
set
{
if (value != _football)
{
_football = value;
this.OnPropertyChanged("Football");
}
}
}
//... and the same for Golf and Hockey
}`
and then you can implement Binding in XAML as
<CheckBox IsChecked="{Binding Football, Mode=TwoWay}"
Command="{Binding Path=SportsResponseCommand}"
CommandParameter="Football"
Content="Football"
Margin="5" />
<CheckBox IsChecked="{Binding Golf, Mode=TwoWay}"
Command="{Binding Path=SportsResponseCommand}"
CommandParameter="Football"
Content="Football"
Margin="5" />

Categories

Resources