When does binding actually happen - c#

On a popup window I have a checkbox.IsChecked bound to a model, but I want to check its state from the xaml code behind when the window is displayed. When checking the checkbox by name in the code behind from the windows loaded event it is not set yet. There are some UI specific things that I need to do and that is why I need to know what the checkboxes value is when the window opens and i cannot perform this from the model that the checkbox.IsChecked is bound to.
The property on the model is set long before the popup window is opened, so it is not an issue of the binding not being there. I figured that once the Loaded event fires the window would be ready to use bindings and all, but this does not seem to be the case.
Xaml:
<RefinedRibbonControls:RefinedRibbonGroup Header="Show Graphs">
<StackPanel x:Name="panelVisibilities">
<CheckBox Content="Show/hide" x:Name="myCheckBox"
IsChecked="{Binding Path=Processor.Model.IsItemVisible}"
Click="GraphVisibilityClickEvent"
HorizontalAlignment="Left"/>
...etc
Property on model:
public bool IsItemVisible
{
get { return _isItemVisible ; }
set
{
if (_isItemVisible != value)
{
_isItemVisible = value;
_propertyChanger.FirePropertyChanged(this, m => m.IsItemVisible);
}
}
}
Event in Xaml codebehind:
private void WindowLoadedEvent(object sender, RoutedEventArgs e)
{
if(myCheckBox.IsChecked.Value)
{
// Do UI Related Stuff
}
}
The binding works fine and the values show up when the window is displayed, the problem is I cannot get the value of the binding in the window loaded event.
Edit: Possible solution I have found but I am not sure if its the best way.
I called the following method from the constructor on the xaml code behind.
private void SetupInitialVisibility()
{
//Fire after everything is loaded.
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() =>
{
IEnumerable<CheckBox> elements = this.panelVisibilities.Children.OfType<CheckBox>().ToList();
foreach (CheckBox checkBox in elements)
{
if (checkBox.IsChecked != null && checkBox.IsChecked.Value == false)
{
//Do work
}
}
}));
}
found at: https://stackoverflow.com/a/1746975/1253746

Data binding is not done synchronously, but it is delayed. Check this msdn page on dispatcher priorities. It is done at a lower priority than normal window messages, but before rendering.
You could invoke a method on yourself with a lower priority than is defined for databinding, in this method you should be able to safely read the data bound value.
I would still find this ugly. I'd rather subscribe directly to PropertyChanged and check for this property, or even better, rewrite your "UI related code" as a data binding.
P.S. If you start consuming events, be sure to unsubscribe, or you might get memory leaks.

DataBinding should precede the Loaded event I think.
When and how do you set your DataContext? And you are positive that the viewmodel property is already set?
The following works, try to align your code with this if possible.
Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<CheckBox x:Name="myCheckBox" IsChecked="{Binding IsItemVisible}" />
</Grid>
</Window>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
if (myCheckBox.IsChecked.Value)
{
//...
}
}
}
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isItemVisible;
public bool IsItemVisible { get { return isItemVisible; } set { isItemVisible = value; OnPropertyChanged("IsItemVisible"); } }
public ViewModel()
{
this.IsItemVisible = true;
}
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

Related

Silverlight Checkbox binding - change checked state in View Model property

I'm pulling what's remaining of my hair out trying to get something to work in Silverlight that works out of the box in WPF.
I have some CheckBoxes on a form which represent items - The Checked property is bound to a bool in my ViewModel (one viewmodel per item). When the checkbox is checked it adds the item to a list in another ViewModel - before this happens I want to perform some validation (in my case count how many items are in the list in the other ViewModel, and if its reached a limit show the user a message) and if this validation fails don't add it to the list and uncheck the box. When it runs, after the validation check is done in the bool property setter I can see the value set back to false, but this is not reflected back in the CheckBox in the UI in Silverlight so the CheckBox remains checked. In WPF this issue does not occur.
The following code demonstrates the issue - for brevity instead of performing the validation I'm just always forcing the box to unchecked when it is checked.
XAML
<UserControl x:Class="CheckboxTest.SL.MainPage"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" Margin="20"/>
</Grid>
</UserControl>
Code Behind
namespace CheckboxTest.SL
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.DataContext = new MainPageViewModel();
}
}
public class MainPageViewModel : INotifyPropertyChanged
{
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
if (_isSelected) //if checked always uncheck
_isSelected = false; //this does not get reflected in UI in Silverlight
OnPropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler changedEventHandler = this.PropertyChanged;
if (changedEventHandler == null)
return;
changedEventHandler((object)this, new PropertyChangedEventArgs(propertyName));
}
}
}
I know that people used to get round this issue when it was in WPF pre .NET 4.0 by either setting the property binding to Async (not allowed in Silverlight) or to implement a dummy IValueConverter (tried this and it had no effect in Silverlight).
Can anyone suggest a way to get the above to work in Silverlight please?
One way to do this could be to bind the IsEnabled property of the CheckBox to a boolean property in your ViewModel where you can perform the validation.
public bool CanEditCheckBox
{
get
{
// perform validation here
return list.Length < 100;
}
}
Edit
I tested your code, and it seems to work like intended.

MouseButtonEventArgs.MouseDevice.DirectlyOver misunderstanding

I was faced with the next misunderstanding.
Preamble:
I have wpf application with next essential UI parts: RadioButtons and some control that use dropdown based on Popup (in combobox manner). According to some logic every radiobutton hook PreviewMouseDown event and do some calculations.
In the next scenario,
User opens popup (do not select something, popup just staying open)
User click on radiobutton
PreviewMouseDown will not be fired for radiobutton as expected (because of Popup feature).
And my aim is firing PreviewMouseDown for RadioButton despite of one.
Attempts to solve:
Fast and dirty solution is: hook PreviewMouseDown for Popup and re-fire PreviewMouseDown event with new source if required, using radiobutton as source. New source can be obtained via MouseButtonEventArgs.MouseDevice.DirectlyOver. The next piece of code do that (event is re-fired only if Popup "eat" PreviewMouseDown for outer click):
private static void GrantedPopupPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var popup = sender as Popup;
if(popup == null)
return;
var realSource = e.MouseDevice.DirectlyOver as FrameworkElement;
if(realSource == null || !realSource.IsLoaded)
return;
var parent = LayoutTreeHelper.GetParent<Popup>(realSource);
if(parent == null || !Equals(parent, popup ))
{
e.Handled = true;
var args = new MouseButtonEventArgs(e.MouseDevice,
e.Timestamp,
e.ChangedButton)
{
RoutedEvent = UIElement.PreviewMouseDownEvent,
Source = e.MouseDevice.DirectlyOver,
};
realSource.RaiseEvent(args);
}
}
This works well when I'm attaching that handler to Popup.PreviewMouseDown directly via Behavior and do not work (PreviewMouseDown isn't fired for radiobutton) if I'm attaching one via EventManager.RegisterClassHandler (aim is to avoid attaching behavior to every Popup that can occure on page with these radiobuttons):
EventManager.RegisterClassHandler(
typeof (Popup),
PreviewMouseDownEvent,
new MouseButtonEventHandler(GrantedPopupPreviewMouseDown));
Debugger showed that e.MouseDevice.DirectlyOver (see code above) is Popup, not Radiobutton (as it is was when I've attached handler via Behavior)!
Question:
How and whyMouseButtonEventArgs can be different for the same action, if eventhandler attached in two different ways?
Can someone explaing this behavior?
Thanks a lot.
The combo box is provided as a way for users to select from a group of options, and you likely want to do that. But it also has other contracts. It says that the user should be focused on this and only this task. But that is not your situation. You want to show the options, have them hide able, and allow the user to do other things while they are shown.
I think instead of combo boxes you want some other control. My suggestion is to use an expander that contains a listbox. Given:
class NotificationObject : INotifyPropertyChanged
{
public void RaisePropertyChanged(string name)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
class ComboEntry : NotificationObject
{
public string Name { get; private set; }
private string _option = "Off";
public string Option
{
get { return _option; }
set { _option = value; RaisePropertyChanged("Option"); }
}
public ComboEntry()
{
Name = Guid.NewGuid().ToString();
}
}
class MyDataContext : NotificationObject
{
public ObservableCollection<ComboEntry> Entries { get; private set; }
private ComboEntry _selectedEntry;
public ComboEntry SelectedEntry
{
get { return _selectedEntry; }
set { _selectedEntry = value; RaisePropertyChanged("SelectedEntry"); }
}
public MyDataContext()
{
Entries = new ObservableCollection<ComboEntry>
{
new ComboEntry(),
new ComboEntry(),
new ComboEntry()
};
SelectedEntry = Entries.FirstOrDefault();
}
public void SetOption(string value)
{
Entries
.ToList()
.ForEach(entry => entry.Option = value);
}
}
I think you want the following XAML:
<Window x:Class="RadioInCombo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RadioInCombo"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:MyDataContext x:Key="myDataContext" />
<DataTemplate x:Key="ComboEntryTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<Border Width="5" />
<TextBlock Text="{Binding Option}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel DataContext="{StaticResource myDataContext}">
<RadioButton x:Name="OnButton"
Content="On"
PreviewMouseDown="OnButton_PreviewMouseDown" />
<RadioButton x:Name="OffButton"
Content="Off"
PreviewMouseDown="OffButton_PreviewMouseDown" />
<Expander Header="{Binding SelectedEntry}"
HeaderTemplate="{StaticResource ComboEntryTemplate}">
<ListBox ItemsSource="{Binding Entries}"
ItemTemplate="{StaticResource ComboEntryTemplate}" />
</Expander>
</StackPanel>
</Window>
And the following code-behind:
private MyDataContext GetMyDataContext()
{
var candidate = FindResource("myDataContext") as MyDataContext;
if (candidate == null) throw new ApplicationException("Could not locate the myDataContext object");
return candidate;
}
private void OnButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
GetMyDataContext().SetOption("On");
}
private void OffButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
GetMyDataContext().SetOption("Off");
}

WPF DataBinding Issues - Possible Noob Problems

I am trying to bind a ViewModel property of type Visibility to the visibility property on a Dock Panel:
Updated ViewModel Code:
public class SelectWaferButtonViewModel : INotifyPropertyChanged
{
private bool isClicked;
public SelectWaferButtonViewModel()
{
isClicked = false;
}
public bool IsControlVisible
{
get
{
return isClicked;
}
set
{
isClicked = value;
OnPropertyChanged("IsControlVisible");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnButtonClick()
{
if (isClicked)
{
IsControlVisible = false;
}
else
{
IsControlVisible = true;
}
}
protected virtual void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
and here is my updated XAML code:
<DockPanel
Name="tvwDockPanel"
Width="200"
Visibility="{Binding IsControlVisible, FallbackValue=Collapsed, Converter={StaticResource BoolToVisConverter}}"
DockPanel.Dock="Left">
<DockPanel
DockPanel.Dock="Top"
Height="22">
</DockPanel>
and I set the data context in the code behind with this line:
tvwDockPanel.DataContext = btnSelectWaferViewModel;
where btnSelectWaferViewModel is the ViewModel object for this situation.
and for fun, here is my code behind:
public partial class WaferTrackerWindow : Window
{
List<ISubscribeEvents> subscriptionList;
SelectWaferButtonViewModel btnSelectWaferViewModel;
public WaferTrackerWindow()
{
InitializeComponent();
this.InstantiateObjects();
this.SubscribeEvents();
this.SetDataContexts();
}
#region Methods
private void SetDataContexts()
{
tvwDockPanel.DataContext = btnSelectWaferViewModel.IsControlVisible;
}
private void SubscribeEvents()
{
foreach (ISubscribeEvents subscriber in subscriptionList)
{
subscriber.SubscribeEvents();
}
}
private void InstantiateObjects()
{
btnSelectWaferViewModel = new SelectWaferButtonViewModel();
subscriptionList = new List<ISubscribeEvents>();
subscriptionList.Add(
new Classes.WaferTrackerWindow.SelectWaferButtonView(btnSelectWafer, btnSelectWaferViewModel));
}
#endregion
}
All I want to do click the button btnSelectWafer and have the tvwDockPanel's visibility property to get to set to Visible via binding. Then when you click again on btnSelectWafer, tvwDockPanel's visibility property gets set back to Collapsed again. tvwDockPanel's visibility will only ever be either Collapsed or Visible.
Any help would be awesome, I am rather new to this whole data binding concept.
You have several issues here:
First of all, the intent of MVVM (if you're trying to do this with MVVM) is to separate logic from presentation. This means that in no way your ViewModel can have a reference to System.Windows.Controls.Button, nor to System.Windows.Visibility, nor to any other classes inside the System.Windows Namespace.
It is not clear to me what your SelectWaferButtonViewModel class is doing with the Button, but you need to remove the Button from there.
Also, If you need to manipulate the Visibility of a control from the ViewModel layer, you'd better use a Boolean property and the BooleanToVisibilityConverter in XAML:
ViewModel:
public bool IsControlVisible {get;set;} //Don't forget INotifyPropertyChanged!!
XAML:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter"/>
</Window.Resources>
<DockPanel Visibility="{Binding IsControlVisible, Converter={StaticResource BoolToVisConverter}}"/>
The problem is that you're binding your DockPanel to the boolean property of your view model, and then setting the Visiblity property of your UI element to the IsControlVisible property of the datacontext (which doesn't exist).
Change to:
private void SetDataContexts()
{
tvwDockPanel.DataContext = btnSelectWaferViewModel;
}

WPF binding problem with certain property names

I've stumbled over the following problem. I've got a checkbox whose IsChecked property is bound to a CLR property in my MainWindow class. Here's the source code.
Code behind (MainWindow.xaml.cs):
namespace MenuItemBindingTest {
public partial class MainWindow : Window, INotifyPropertyChanged {
private bool m_backedVariable = false;
public bool IsPressAndHoldEnabled {
get { return this.m_backedVariable; }
set {
this.m_backedVariable = value;
OnPropertyChanged("IsPressAndHoldEnabled");
MessageBox.Show("Item changed: " + this.m_backedVariable);
}
}
public MainWindow() {
InitializeComponent();
this.m_checkbox.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) {
if (this.PropertyChanged != null) {
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
XAML code (MainWindow.xaml):
<Window x:Class="MenuItemBindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Binding Problem Test" Width="525" Height="350">
<DockPanel>
<CheckBox x:Name="m_checkbox"
IsChecked="{Binding IsPressAndHoldEnabled}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Content="Is Press and Hold enabled"/>
</DockPanel>
</Window>
The problem now is that the set accessor for the property IsPressAndHoldEnabled is never called (ie. the message box never shows) when the user checks or unchecks the check box. It does, however, work when I rename the property to something else - like IsPressAndHoldEnabled2.
My question now is: Why can't I use IsPressAndHoldEnabled as name for my property? Does this have anything to do with the property Stylus.IsPressAndHoldEnabled existing?
Interesting. I don't have answers why, but I have workarounds:
Seperating the IsPressAndHoldEnabled property out to a seperate ViewModel class worked, unless the class was derived from FrameworkElement.
Also, changing from a regular property to a Dependency Property in the same MainWindow class worked -- the DP changed callback fires.
Have you specified TwoWay as your binding mode? Although I think CheckBox.IsChecked defaults to TwoWay binding mode...
I think you may have messed up your binding context, so that it is not finding the IsPressAndHoldEnabled property. Bindings in WPF fail silently -- a royal pain if you ask me.
Check that the check-box is really bound to that property, and that the binding context really is your MainWindodw class object.

Bind a control to a single value in a collection/array in WPF

In WPF I have a collection of bool? values and I want to bind each of these to a separate checkbox programmatically. I want the bindings to be TwoWay so that changing the value of the individual item in the collection in code updates the check box and vice versa.
I have spent ages trying to figure out how to do this and I am completely stuck. With the following code the checkbox only gets the right value when the window is loaded and that's it. Changing the check box doesn't even update the value in the collection. (UPDATE: this appears to be a bug in .NET4 as the collection does get updated in an identical .NET3.5 project. UPDATE: Microsoft have confirmed the bug and that it will be fixed in the .NET4 release.)
Many thanks in advance for your help!
C#:
namespace MyNamespace
{
public partial class MyWindow : Window, INotifyPropertyChanged
{
public MyWindow()
{
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public List<bool?> myCollection = new List<bool?>
{ true, false, true, false, true, false };
public List<bool?> MyCollection
{
get { return myCollection; }
set { myCollection = value; }
}
}
}
XAML:
<CheckBox IsChecked="{Binding Path=MyCollection[0], Mode=TwoWay}">
There are a few things that need changing here to get this to work. Firstly you'll need to wrap your boolean value in an object that implements the INotifyPropertyChanged interface in order to get the change notification that you are looking for. Currently you are binding to boolean values in your collection which do not implement the interface. To do this you could create a wrapper class like so :
public class Wrapper: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool val = false;
public bool Val
{
get { return val; }
set
{
val = value;
this.OnPropertyChanged("Val");
}
}
public Wrapper(bool val)
{
this.val = val;
}
}
You'll then want to create these objects in your form instead of a list of booleans. You may also want to use an observable collection instead of a list so that notification of items being added and removed are sent. This is shown below:
public Window1()
{
InitializeComponent();
this.DataContext = this;
}
private ObservableCollection<Wrapper> myCollection = new ObservableCollection<Wrapper>()
{new Wrapper(true), new Wrapper(false), new Wrapper(true)};
public ObservableCollection<Wrapper> MyCollection
{
get { return myCollection; }
}
The next thing to do is to display a list of check boxes in your ui. To do this WPF provides itemscontrols. ListBox is an itemscontrol so we can use this as a starting point. Set the itemssource of a listbox to be MyCollection. We then need to define how each Wrapper object is going to be displayed in the list box and this can be done with a datatemplate which is created in the windows resources. This is shown below :
<Window.Resources>
<DataTemplate x:Key="myCollectionItems">
<CheckBox IsChecked="{Binding Path=Val, Mode=TwoWay}"></CheckBox>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Path=MyCollection}" ItemTemplate="{StaticResource myCollectionItems}"></ListBox>
</Grid>
This should get you up and running with a simple demo of checkboxes that have values bound to a list of booleans.
What makes you think it's not working? It's working for me :)
Here's my test XAML:
<UniformGrid>
<CheckBox IsChecked="{Binding Path=MyCollection[0], Mode=TwoWay}"/>
<ListBox ItemsSource="{Binding MyCollection}"/>
<Button Content="Test" Click="Button_Click"/>
</UniformGrid>
Here's my code behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
}
(the rest is the same as yours)
I placed a breakpoint on Button_Click and checked MyCollection[0] it was updated according to the IsChecked value of the CheckBox.
Try changing your collection type from List<bool?> to ObservableCollection<bool?> perhaps that is the reason you think it's not working for you (the fact that changes to the collection are not reflected anywhere else in your view).
Change your List<bool?> to an ObservableCollection<bool?>. A List does not raise the change notifications that WPF needs to update the UI. An ObservableCollection does. This handles the case where the list entry is changed and the CheckBox needs to update accordingly.
In the other direction, it works for me even with a List<bool?> -- i.e. toggling the checkbox modifies the value in the collection. Your binding syntax is certainly correct.

Categories

Resources