If I have a WPF view with a textbox that has a binding to a decimal (or any other number format) I automatically get a visual hint if I enter a letter or any other invald character and the value is not transferred to the viewmodel (the breakpoint on the setter is never reached). If I enter a number, everything works fine. To disable my save-Button (ICommand), I would like to get the info in my viewmodel that there is an error in the view in a MVVM-like fashion. Hints to where this behavior is documented are very welcome!
So the target situation looks like this:
what I want would be a disable "save and close":
XAML:
<TextBox Text="{Binding Path=SelectedItem.Punkte_Seite_max, UpdateSourceTrigger=PropertyChanged}"/>
ViewModel
public int Punkte_Seite_max
{
get { return _punkte_Seite_max; }
set
{
_punkte_Seite_max = value;
Changed(); //INotifyPropertyChanged call
}
}
What you want to be using is INotifyDataErrorInfo documentation found here. This lets you provide custom validation on the properties that you have bound to your ViewModel.
This is a sample I have shamelessly copied from CodeProject but I have done so to prevent any link rot. I have also tried to adapt it slightly to match your example.
ViewModel
public class ViewModel : INotifyDataErrorInfo
{
// A place to store all error messages for all properties.
private IDictionary<string, List<string>> propertyErrors = new Dictionary<string, List<string>>();
public string Preis
{
get { return _preis; }
set
{
// Only update if the value has actually changed.
if (!string.Equals(_preis, value, StringComparison.Ordinal))
{
_preis = value;
Changed();
this.Validate();
}
}
}
// The event to raise when the error state changes.
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
// A method of getting all errors for the given known property.
public System.Collections.IEnumerable GetErrors(string propertyName)
{
if (propertyName != null)
{
if (propertyErrors.TryGetValue(propertyName, out var errors))
{
return errors;
}
}
return null;
}
// Whether there are any errors on the ViewModel
public bool HasErrors
{
get
{
return propertyErrors.Values.Any(r =>r.Any());
}
}
private void Validate()
{
// 1. HERE YOU CAN CHECK WHETHER Preis IS VALID AND ANY OTHER PROPERTIES
// 2. Update the 'propertyErrors' dictionary with the errors
// 3. Raise the ErrorsChanged event.
}
}
XAML
You will need to change your XAML to something like this:
<TextBox>
<Binding Path="Preis" UpdateSourceTrigger="PropertyChanged" ValidatesOnNotifyDataErrors="True"/>
</TextBox>
Thanks to Bijington I got on the right track and found an answer which satisfies MVVM and also doesn't need code behind. In case someone is interested here's my solution to this issue.
The error shown above is created in the view because there is no converter in WPF from letters to int (how should there be one). To raise this issue the binding in needs to have NotifyOnValidationError=True.
<TextBox Text="{Binding Path=SelectedItem.Punkte_Seite_max, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True}"
This raises a bubbling up Validation.Error event that can be captured anywhere in the tree. I decided to capture it via a routed event trigger like so:
XAML:
<Window
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" >
<i:Interaction.Triggers>
<userInterface:RoutedEventTrigger RoutedEvent="{x:Static Validation.ErrorEvent}" >
<userInterface:ViewErrorCounterAction ViewErrorCounter="{Binding Path=ViewValidationErrorCount, Mode=TwoWay}"/>
</userInterface:RoutedEventTrigger>
</i:Interaction.Triggers>
So the twoway-binding is the MVVM-okayish link to my viewmodel.
ViewErrorCounterAction is based on this SO answer:
public class ViewErrorCounterAction : TriggerAction<DependencyObject> {
public ViewErrorCounterAction()
{
ViewErrorCounter = 0; // initalize with 0 as there should not be such errors when the window is loaded
}
public int ViewErrorCounter
{
get
{
return System.Convert.ToInt32(GetValue(ViewErrorCounterProperty));
}
set
{
SetValue(ViewErrorCounterProperty, value);
}
}
public static readonly DependencyProperty ViewErrorCounterProperty = DependencyProperty.Register("ViewErrorCounter", typeof(int), typeof(ViewErrorCounterAction), new PropertyMetadata(null));
protected override void Invoke(object parameter)
{
var e = (ValidationErrorEventArgs)parameter;
if ((e.Action == ValidationErrorEventAction.Added))
ViewErrorCounter = ViewErrorCounter + 1;
else if ((e.Action == ValidationErrorEventAction.Removed))
ViewErrorCounter = ViewErrorCounter - 1;
}
}
Finally routed Event Trigger is based on https://sergecalderara.wordpress.com/2012/08/23/how-to-attached-an-mvvm-eventtocommand-to-an-attached-event/
Hope this helps and I'd appreciate comments on how to better solve this issue if there are more elegant ways :)
I know, title is a little confusing so let me explain. I have a user control that has a dependency property. I access this dependency property with a regular property called Input. In my view model I also have a property called Input. I have these two properties bound together in XAML using two-way binding as shown below:
<uc:rdtDisplay x:Name="rdtDisplay" Input="{Binding Input, Mode=TwoWay}" Line1="{Binding myRdt.Line1}" Line2="{Binding myRdt.Line2}" Height="175" Width="99" Canvas.Left="627" Canvas.Top="10"/>
Okay in my view model, I call a method whenever the value of Input is changed as shown in my property:
public string Input
{
get
{
return input;
}
set
{
input = value;
InputChanged();
}
}
The problem with this is that when I set the value of Input in my view model it only updates the value of the variable input as per my setter in my property. How can I get this to update back to the dependency property in the user control? If I leave the code input = value; out then I get a compilation error.
I need something like this:
public string Input
{
get
{
return UserControl.Input;
}
set
{
UserControl.Input = value;
InputChanged();
}
}
If I make the Input property in my view model look like this:
public string Input
{
get; set;
}
then it works, however, I am unable to call the InputChanged() method that I need to call when the Property is changed. All suggestions are appreciated.
Implement INotifyPropertyChanged in your ViewModel
public class Sample : INotifyPropertyChanged
{
private string input = string.Empty;
public string Input
{
get
{
return input;
}
set
{
input = value;
NotifyPropertyChanged("Input");
InputChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
In your case, you can do it in the code behind of your usercontrol
I have a TextBox which I'm trying to bind to a element of a table property 'regimeAlias' is a column with the tbRegimes table which I have mapped with Entity Framework:
<TextBox Text="{Binding NewRegime.regimeAlias, Mode=TwoWay}"/>
Exposed property in my ViewModel:
private tbRegime _NewRegime;
public tbRegime NewRegime
{
get { return _NewRegime; }
set
{
_NewRegime = value;
OnPropertyChanged("NewRegime");
}
}
Lastly, here's the WCF Service Reference auto-generated code class:
public partial class tbRegime : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
//blah blah blah
[System.Runtime.Serialization.DataMemberAttribute()]
public string regimeAlias {
get {
return this.regimeAliasField;
}
set {
if ((object.ReferenceEquals(this.regimeAliasField, value) != true)) {
this.regimeAliasField = value;
this.RaisePropertyChanged("regimeAlias");
}
}
}
The setter never gets hit. Is this because each element within the NewRegime object needs to raise PropertyChanged and if so is there an easy workaround without adding a further DTO layer to my code?
Edit3: with the post from your regimeAlias code. i have to say your binding should work. but of course if you wanna debug you have to set the breakpoint in your regimeAlias setter
<TextBox Text="{Binding NewRegime.regimeAlias, Mode=TwoWay}"/>
this code means, you bind to a Public Property regimeAlias in your class tbRegime.
your setter for NewRegime will never hit because you dont bind to it.
so check your tbRegime class property setter for regimeAlias.
EDIT: the DataContext of the TextBox is of course an object with the Public Property NewRegime, but like i said if you use dot notation in your binding the last property is the one you bind to :)
EDIT: you dont have much ways to workaround:) if you let the binding like you did, you need a model with a public property regimeAlias and it should implement INotifyPropertyChanged.
if you wanna wrap the regimeAlias Property then you have the problem the you have to raise OnPropertyChanged("MyRegimeAlias") at the right point.
public string MyRegimeAlias
{
get { return _NewRegime.regimeAlias; }
set
{
_NewRegime.regimeAlias = value;
OnPropertyChanged("MyRegimeAlias");
}
}
xaml
<TextBox Text="{Binding MyRegimeAlias, Mode=TwoWay}"/>
I am trying to create a list box that displays a set of data that will be updated over time. I have a simple list box:
<ListBox Name="lbRegisters" ItemsSource="{Binding}" />
And I have defined a class for my objects:
public class register : INotifyPropertyChanged
{
private int address;
public int Address { get { return address; } }
private int value;
public int Value
{
get{ return value; }
set{
this.value = value;
OnValueChanged("Value");
}
}
public register(int a)
{
address = a;
value = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnValueChanged(string name){
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
public override string ToString()
{
return address + ": " + value;
}
}
And I hold a list of these in an ObservableCollection<register>. Then set the ListBox.ItemsSource=registerslist; in the CodeBehind. The list is initialized and the inital data displays correctly.
Now what do I need to do to get my ListBox to update when a "register.Value" changes. The event handler is called but there nothing is subscribed to the event.
I guess I need to trigger something in the ListBox or ObservableCollection to tell the GUI to update. I have read dozens of posts of a similar problem but they all seem to indicate that once you have implemented INotofyPropertyChanged it just automagicaly works.
What is the next step?
The problem is on your ToString() function. Yes it could be used to display complex string in ListView items but it is not a proper way to bindings because ListView does not knows when part of this string was changed.
Do the following:
1. Declare property on register class like
public string AddressValue
{
get { return address + ": " + value; }
}
2. Add OnValueChanged("AddressValue") in value and address setters like:
public int Value
{
get{ return value; }
set{
this.value = value;
OnValueChanged("Value");
OnValueChanged("AddressValue")
}
}
3. Declare you ListBox with ItemTemplate like:
<ListBox x:Name="lbRegisters" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding AddressValue}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So the idea is adding new property to register class which will be updated when address and value changed. And bind list item text to this property.
I think understand the spirit of what you're asking (even though it's obviously unfinished ATM). I'd recommend using something that inherits from DependencyObject and using dependency properties. Use the propdp snippet in Visual Studio. It will save you a ton of boilerplate code and wiring.
This is probably pretty basic, but I'm just picking up C# after many years working with other languages, and I've unfortunately grown used to loose and dynamic typing. Now, in building a WPF form with a lot of checkboxes, I notice that my code to do simple things like determine whether a checkbox is checked is actually quite complicated due to the need to check for nulls and cast the results. I end up with helper functions like:
private bool isChecked(CheckBox control) {
return control != null && control.IsChecked != null && control.IsChecked.HasValue && (bool)control.IsChecked;
}
so that in my logic code, I can just do
if (isChecked(opticsCheckBox))
{
// whatever I need to do if opticsCheckBox is checked
}
Is this the normal way of doing things in C# (with WPF), or am I missing something simple? Basically I'm finding the nested layers of conditionals to check every object for null all the time to be a warning sign of bad code (and the fact that I could forget a check). Not sure what I should be doing though.
Should I be using try ... catch everywhere even though the control not being present or checked isn't really an exceptional condition? That seems to me like it would end up being just as cluttered.
Another example to clarify:
When I would like to write something like:
maxWeight = (int)maxWeightComboBox.SelectedItem;
I find myself instead writing:
if (maxWeightComboBox != null && maxWeightComboBox.SelectedItem != null)
{
ComboBoxItem item = (ComboBoxItem)maxWeightComboBox.SelectedItem;
maxWeight = Int32.Parse(item.Content.ToString());
}
WPF provides such features as notifications on property changes, dependency propeties, and binding.
So the good practice in WPF is to use PresentationModel-View pattern or MVC pattern instead of direct access to controls.
Your presentation model (or contoller) have to handle all business logic, and view just reflect actual state of model.
In your case model looks like:
public class SampleModel : ObservableObject
{
private bool? _isFirstChecked;
public bool? IsFirstChecked
{
get
{
return this._isFirstChecked;
}
set
{
if (this._isFirstChecked != value)
{
this._isFirstChecked = value;
this.OnPropertyChanged("IsFirstChecked");
}
}
}
private int _maxWeight;
public int MaxWeight
{
get
{
return this._maxWeight;
}
set
{
if (this._maxWeight != value)
{
this._maxWeight = value;
this.OnPropertyChanged("MaxWeight");
}
}
}
public IEnumerable<int> ComboBoxItems
{
get
{
yield return 123;
yield return 567;
yield return 999;
yield return 567;
yield return 1999;
yield return 5767;
yield return 9990;
}
}
}
As we have to notify view with property changed event, we add Observable class, which implement this logic:
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var safePropertyChanged = this.PropertyChanged;
if (safePropertyChanged != null)
{
safePropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
So, now we have presentation model with declaration of necessary properties, let's see at view:
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self ="clr-namespace:Test"
Title="MainWindow"
Height="350" Width="525">
<Window.Resources>
<self:NullableBoolToStringConvreter x:Key="nullableBoolToStringConverter" />
</Window.Resources>
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label VerticalAlignment="Center">IsFirstChecked:</Label>
<CheckBox VerticalAlignment="Center"
IsChecked="{Binding Path=IsFirstChecked}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label VerticalAlignment="Center">Max Weight:</Label>
<ComboBox ItemsSource="{Binding Path=ComboBoxItems}"
VerticalAlignment="Center"
SelectedValue="{Binding Path=MaxWeight}">
</ComboBox>
</StackPanel>
<TextBox Text="{Binding Path=MaxWeight}" />
<TextBox Text="{Binding Path=IsFirstChecked, Converter={StaticResource nullableBoolToStringConverter}}"/>
<Button Click="Button_Click" Content="Reset combo box to 999 and checkbox to null"/>
</StackPanel>
</Grid>
Also we have to modify this xaml code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var model = new SampleModel();
model.MaxWeight = 5767;
this.Model = model;
}
public SampleModel Model
{
get
{
return (SampleModel)this.DataContext;
}
set
{
this.DataContext = value;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Model.MaxWeight = 999;
this.Model.IsFirstChecked = null;
}
}
As you can see we create SampleModel instance at MainWindow constructor, set up its properties and set model instance as DataContext of view.
After DataContext changed, WPF internal mechanizm starts binding process. For example, for combobox control it extracts model property ComboBoxItems and creates item containers. Then extracts property MaxValue and bind it to SelectedValue, i.e. combobox selection will point at value '5767'.
In demostration purposes I placed two text boxes, which display actual value of "MaxWeight" and "IsFirstChecked" properties. Default binding implementation shows empty strings on null values, so we have to add appropriate converter:
public class NullableBoolToStringConvreter : IValueConverter
{
private static readonly string _NullString = "Null";
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value == null ? NullableBoolToStringConvreter._NullString : value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can test application and ensures that changes of UI controls state automatically reflects in model. On the other hand, clicking on the button will reset model properties to defined values and UI immediatly reacts on it.
So with WPF you don't need access to controls. XAML and InitializeComponent() guarantee you that all controls are created.
As for checking:
control.IsChecked.HasValue && (bool)control.IsChecked
as was mentioned you can use expression
model.IsFirstChecked ?? false
or extension method:
public static class BooleanNullableExtensions
{
public static bool IsTrue(this Nullable<bool> value)
{
return value.HasValue && value.Value;
}
}
In general, yes, C# is a bit more verbose than a dynamic / lossely typed language. The same is true of Java. Looking at your specific examples ...
private bool isChecked(CheckBox control) {
return control != null && control.IsChecked != null && control.IsChecked.HasValue && (bool)control.IsChecked;
}
A couple of points ... the following two checks are equivalent:
control.IsChecked != null
control.IsChecked.HasValue
The IsChecked property is a Nullable type. As you are new to C# I would recommend reading up on value types vs. reference types. Once you have the hang of that, you can find out how the Nullable type can be used to wrap a value type in order to assign a null value to it. The page linked below explains why the twp statements above are equivalent:
http://msdn.microsoft.com/en-us/library/2cf62fcy%28VS.80%29.aspx
Secondly, why are you checking that control!=null ? In typical scenarious you create controls in XAML on your Window or UserControl, identifying via the x:Name attribute. In this case you can rely on the control being present in your UI and drop this check.
Your other two checks are necessary ;-)
Good idea placng these in a method that you can re-use. You can also 'extend' the langauge by creating extension methods, e.g
private bool IsChecked(this CheckBox control) {
return control.IsChecked.HasValue && (bool)control.IsChecked;
}
// calls the extension method above.
myCheckBox.IsChecked()
Hoep that helps.
There are a lot of ways to answer your question. I think the most important of these ways is to emphasize that in WPF, if you're writing code that explicitly manipulates UI controls, you're probably doing something wrong.
I can't emphasize this enough. To my mind the primary reason to use WPF at all is that it frees you from having to manipulate the UI in code.
Here's how my programs determine whether or not a checkbox is checked:
In XAML:
<CheckBox IsThreeState="false" IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
In the object bound to this view:
public bool IsChecked { get; set; }
The IsChecked property of my object now always reflects the state of the checkbox in the UI. (The reverse is not true unless I implement change notification in my class.)
For your combo box example, I'd implement it like this. First, in XAML:
<ComboBox ItemsSource="{Binding Numbers}" SelectedItem="{Binding SelectedNumber, Mode=TwoWay}"/>
In the object bound to the view:
public IEnumerable<int> Numbers { get { return new[] { 1, 2, 3, 4, 5, 6 }; } }
public int? SelectedNumber { get; set; }
SelectedNumber is nullable in this case so that you can test for the case where nothing was selected, e.g.:
Console.WriteLine(SelectedNumber == null
? "No number was selected."
: SelectedNumber + " was selected.);
With nullable types (including references), you can use the ?? operator to specify a default value to be used if the object is null. So control.IsChecked != null && control.IsChecked could be replaced by control.IsChecked ?? false. This doesn't solve all your problems, but it helps to reduce the amount of code you type in some cases.
A less verbose form is
control != null && control.IsChecked == true
Remember, a bool? has three values, true, false, and null, and it's always sufficient to check against a single value. For example, a == true and a != false are the checks, respectively, for when the null works like a false or when the null works like a true.
For your combobox example, I'd use a strongly typed collection to begin with. See wpf: how to make ComboBoxItems hold integers in xaml for an example of how to bind directly to integers, (or if you want separate Content/Value, bind it to a list of KeyValuePairs [for example]) Then use SelectedValue and SelectedValuePath to reduce your value-retrieval code.