set IsEnabled = false a button in silverlight - c#

I have the following code in the ViewModel class, in the constructor where I define that the buttons are always Enabled = false when starting the form ...
public partial class EditarConceptoWindow : ChildWindow
{
public EditarConceptoWindow(string documentoId)
{
InitializeComponent();
viewModel.Saved += new EventHandler<Microsoft.Practices.Prism.Events.DataEventArgs<bool>>(ViewModel_Saved);
viewModel.Calculation += new EventHandler<Microsoft.Practices.Prism.Events.DataEventArgs<bool>>(ViewModel_Calculation);
this.DataContext = viewModel;
BtnCalcular.IsEnabled = false;
BtnObtenerTCRM.IsEnabled = false;
....... rest of code
In a checked event of a check box when placing the Selected check box, it must be enabled to be set to true, depending on whether a particular element of a combobox has been selected as well;
private void cbAgregarManual_Checked(object sender, RoutedEventArgs e)
{
if (this.ComboConcepto.SelectedValue.ToString() == "DPI")
{
BtnCalcular.IsEnabled = true;
BtnObtenerTCRM.IsEnabled= true;
}
}
This must be done if and only if the checkbox is clicked and the DPI value is selected in the combobox.
But the behavior of the buttons is that when starting the form they are always IsEnabled = true and if the checkbox control is clicked if it works but I can't find a reason because only until I click the checkbox it works, there are some controls (such as TextBoxes, and also the buttons) with this directive in the XAML.
IsEnabled="{Binding ElementName=cbAgregarManual, Path=IsChecked }"

I suggest that you centralize the logic of the enabling into one property to avoid this mismatch of logic setting confusion.
In this new property it will use INotifyPropertyChanged for the notification of that change, but called in from other properties. So to sum up, when any of the associated values change, they do the notify call on the logic property; that process ensures that the control is properly en/dis-abled.
Example
Such as this pseudo code where I check three different other properties:
public bool IsEnabledCombo { get { return ClickStatus
&& OtherStatus
&& !string.IsNullOrEmpty( UserText); }
public bool ClickStatus { get { return _clickStatus; }
set { _clickStatus = value;
NotifyPropertyChanged("ClickStatus");
NotifyPropertyChanged("IsEnabledCombo");
}}
public bool OtherStatus { get { return _otherStatus; }
set { _clickStatus = value;
NotifyPropertyChanged("OtherStatus");
NotifyPropertyChanged("IsEnabledCombo");
}}
public string UserText { ...
set { _userText = value;
NotifyPropertyChanged("UserText");
NotifyPropertyChanged("IsEnabledCombo");
Bind your control as such
IsEnabled="{Binding IsEnabledCombo }"
So wherever one of the related values can change they also call NotifyPropertyChanged("IsEnabledCombo"); and the control status is updated automatically.
I provide a another notify chained example doing similar on my blog:
Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding

Related

Trigger PropertyChanged when sub member property changes

I have a WPF control that is supposed to be simple to (re)use. For that I have a custom type containing all the settings the control is supposed to represent and bind it over a DependencyProperty.
However, whenever I change one of the members in the control, the parent control gets the changes in the member (when evaluated through other means), but the PropertyChanged-Callback never gets triggered in the parent control.
public class Setting
{
public int Prop {get;set;}
//Other Properties, Constructor & Copy Constructor, etc.
public override bool Equals(object obj)
{
if (!(obj is Setting other)) return false;
return Prop == other.Prop;
}
}
public class SettingControl : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty SettingProperty = DependencyProperty.Register
(nameof(Settings), typeof(Setting), typeof(SettingControl),
new PropertyMetadata(default(Setting), OnValuePropertyChanged));
public Setting Settings
{
get => (Setting)GetValue(SettingProperty);
set
{
SetValue(SettingProperty, value);
OnPropertyChanged(nameof(Settings));
}
}
public int Prop
{
get => ((Setting)GetValue(SettingProperty))?.Prop ?? 0;
set
{
//Does not work:
var temp = (Setting)GetValue(SettingProperty);
temp.Prop = value;
Settings = temp;
OnPropertyChanged(nameof(Prop));
//Does not work:
Settings.Prop = value;
OnPropertyChanged(nameof(Prop));
OnPropertyChanged(nameof(Settings));
//**Does work**, and triggers the OnSettingChanged in the parent control,
//but is simply not great memory usage
Settings = new Setting(Settings){ Prop = value };
OnPropertyChanged(nameof(Prop));
}
}
}
//Relevant snippet from parent Control ViewModel:
public static readonly DependencyProperty SettingProperty =
DependencyProperty.Register(nameof(Settings), typeof(Setting), typeof(ControlViewModel),
new PropertyMetadata(default(Setting), (d, e) => ((ControlViewModel)d).OnSettingChanged()));
//OnSettingChanged() is never called
public Setting Settings
{
get => (Setting)GetValue(SettingProperty);
set //Set is never called when the member properties are changed
{
SetValue(SettingProperty, value);
OnPropertyChanged(nameof(Settings));
}
}
//Relevant snippet from parent xaml:
<local:SettingControl Width="300"
Settings="{Binding Path=Settings, Mode=TwoWay}"/>
// UpdateSourceTrigger=PropertyChanged doesn't help here either
An obvious solution of course would be to either wrap the Setting class into a SettingViewModel, or implement it as a ViewModel itself (small testing didn't show results anyway). This however would make the usage of the control a lot harder, and to some degree break MVVM (more than this already). There are also some XML things in the Setting class for serialization that I don't want to mess with.
One thing I noticed is that if the Equals function in Setting is coded to always return true the two ways of setting the member property that normally don't work, suddenly work and trigger the desired behavior.
Thanks in Advance.

Tracking if a form is dirty in UWP

I'm attempting to track the dirty (modified) status of a form in UWP that uses two way bindings between the view and the model.
My current method of tracking the respective changed events (TextChange for text boxes, SelectionChanged for combo boxes, etc.) works except for when the page is loaded with data.
What appears to be happening is that the bindings are still being evaluated after the page is fully loaded, thus making the form think it has been modified. From stepping through my code I'm building the following typical timeline when a page is loaded:
Model is initialized (dirty = false)
Data is retrieved and placed in the model
Bindings trigger, filling the form (dirty = true)
The Loaded event of the page is hit, at which point I set dirty = false
Bindings continue to be set, thus resetting dirty to true
(I need something here to set dirty back to false)
Is there an event that triggers after all bindings are evaluated for the first time, or some way of tracking whether the bindings have all been evaluated?
Edit: Some rough sample code to give a better idea of how I've got things set up
Model:
public class OrderModel : INotifyPropertyChanged
{
private OrderCase order;
public OrderCase Order
{
get
{
return order;
}
set
{
order = value;
this.RaisePropertyChanged("Order");
}
}
private bool dirty;
public bool Dirty
{
get
{
return dirty;
}
set
{
dirty = value;
}
}
}
View - Code Behind
OrderModel Model;
int OrderId;
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
//Call a method in the View Model which calls a WCF service
//to get the OrderCase object out of our database and into the
//Order object in the model
Model = new OrderModel();
Model.Order = await WcfService.GetOrder(OrderId);
//Once this returns all the bindings start evaluating
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
SetUndirty();
}
public override void TextBoxSetDirty(object sender, TextChangedEventArgs e)
{
//one of these exists for each type of control I use
//this gets triggered when the text changes due to the model changing
SetDirty();
}
public void SetDirty()
{
Model.Dirty = true;
}
View - XAML
<TextBox
Text = "{x:Bind Path=Order.CustomerName, mode=TwoWay}"
TextChanged="TextBoxSetDirty"/>
The view is a lot more of the same, including ComboBoxes, RadioButtons, ToggleSwitches, and a SyncFusion Datagrid
Edit 2:
I currently have a work around in place, another property called "UserHasInteracted" which is initially False, but upon either the page's KeyDown or Tapped events firing, it is set to true, and the SetDirty method is modified as such:
public void SetDirty()
{
if (UserHasInteracted)
Model.Dirty = true;
}
The end result is that the form can't be set to Dirty until the user has performed some sort of interaction upon it.
This works about 75% of the time, and produces no false positives (saying the form is dirty when it isn't), but does produce some false negatives, because the Tapped event doesn't seem to fire when a RadioButton, CheckBox, or ToggleSwitch is clicked. It also fails if the only interaction the user performs is a single key press within a text box, as the TextChanged event fires before the KeyDown, however the chances that our users will only enter a single character into a text box as their modification is slim to none, so this one isn't a major concern.
How is your ViewModel instatiated?
For your class:
public class OrderModel : INotifyPropertyChanged
{
private OrderCase order;
public OrderCase Order
{
get
{
return order;
}
set
{
if (value != order) { IsDirty = true; }
order = value;
this.RaisePropertyChanged("Order");
}
}
private bool dirty;
public bool Dirty
{
get
{
return dirty;
}
set
{
dirty = value;
}
}
}
This should work, or not?
OrderModel Model;
int OrderId;
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
//Call a method in the View Model which calls a WCF service
//to get the OrderCase object out of our database and into the
//Order object in the model
var tempModel = new OrderModel();
tempModel = await WcfService.GetOrder(OrderId);
Model.Order = tempModel;
//Setting the ViewModel's property only when getting order is completed
//Once this returns all the bindings start evaluating
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
SetUndirty();
}
public override void TextBoxSetDirty(object sender, TextChangedEventArgs e)
{
//one of these exists for each type of control I use
//this gets triggered when the text changes due to the model changing
SetDirty();
}
public void SetDirty()
{
Model.Dirty = true;
}
Otherwise you could try to add each single property in your ViewModel instead only one copy of OrderCase... just a thought

WPF : MvvmLight, Usercontrol, Binding

I have a simple usercontrol (DoubleRadioControl2), composed of 2 radio buttons. I have a Dep Prop on this UC : (bool?)IsOuiChecked :
true - 'yes' radio checked
false - 'no' radio checked
null - both
radios unchecked
Pretty simple.
private static readonly DependencyProperty IsOuiCheckedProperty = DependencyProperty.Register("IsOuiChecked", typeof(bool?), typeof(DoubleRadioControl2), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public bool? IsOuiChecked
{
get
{
return (bool)GetValue(IsOuiCheckedProperty);
}
set
{
SetValue(IsOuiCheckedProperty, value);
if (value == null)
{
RadioYes.IsChecked = false;
RadioNo.IsChecked = false;
}
else
{
RadioYes.IsChecked = (bool) value;
RadioNo.IsChecked = !(bool) value;
}
}
}
And the logic between ths 2 radios :
private void OptDecompteConfnOui_Click(object sender, RoutedEventArgs e)
{
IsOuiChecked = true;
}
private void OptDecompteConfnNon_Click(object sender, RoutedEventArgs e)
{
IsOuiChecked = false;
}
When i bind this UC to my viewmodel, the prop binded if updated when i click on my radios. But when i set the value in the code of my view model, it won't update my radios.
In my viewmodel :
private bool? _isDRB2OuiChecked;
public bool? IsDRB2OuiChecked
{
get
{
return _isDRB2OuiChecked;
}
set
{
if (_isDRB2OuiChecked == value)
{
return;
}
_isDRB2OuiChecked = value;
RaisePropertyChanged(() => IsDRB2OuiChecked);
}
}
TwoWay binding not working.
The getters and setters for your dependency property only exist for your (the programmers) convenience. WPF itself will not call them, but set the property directly.
You need to attach a handler to the changed event of the property. Use one of the FrameworkPropertyMetadata constructors, that take a PropertyChangedCallback . Your getter and setter logic needs to be handled there instead of inside the property.
If you are using MVVM you should call OnPropertyChanged
First of all this is a very BAD practice to put code inside the Get/Set parts of a dependency property, look at these exanples to get the solution. If you have the logic on DP changes, you have to put that login inside the callback.
Dependency properties explanation here.
MSDN Overview here.
if there is binding defined at the View/Viewmodel level you have to use a INotifyPropertyChange event at the Viewmodel side to inform View that the binded property was changed.
regards,

Binding ListBox.SelectedItem to Property

This might be a duplicate question, but I'm unable to find a good answer. All the answers like Binding WinForms ListBox to object properties don't work on my WinForm. I'll explain.
I have a list of Firms that I show in a ListBox. I would like when the SelectedItem changes, that it updates a property on my model. So that I can read the Firms properties.
// the classes
public class Firm
{
public string Name { get; set; }
public int Id { get; set; }
// more properties ...
}
public class MyModel : INotifyPropertyChanged
{
private Firm _firm = new Firm();
public Firm Firm
{
get { return _firm; }
set
{
if (Equals(value, _firm)) return;
_firm = value;
OnPropertyChanged();
}
}
// more properties and OnPropertyChanged() ...
}
// the form
private MyModel Model;
public void MyForm(List<Firm> firms)
{
lstFirm.DataBindings.Add("SelectedItem", Model, "Firm",
true, DataSourceUpdateMode.OnPropertyChanged);
lstFirm.DisplayMember = "Name";
lstFirm.ValueMember = "Id";
lstFirm.DataSource = firms;
}
public void lstFirm_SelectedIndexChanged(object sender, EventArgs e)
{
// Do something with Model.Firm
}
The problem is that Model.Firm null is. Does anybody have an idea what I need to do to make a databinding between the ListBox and the Model? I bind other stuff on my WinForm (such as TextBoxes to String properties) and those work nicely.
From what I can see, your code never sets Model.Firm... Where's the constructor for MyModel? If you don't provide one, Model.Firm will stay null unless you explicitly set it. Here's an example constructor:
public MyModel(Firm firm)
{
_firm = firm;
}
Also, Equals() doesn't do what you think it does. Instead of if (Equals(value, _firm)) return;, use this: if (value == _firm) return;
Ok, so after a weekend of testing, I figured it out.
I was debuging in the SelectedIndexChanged event and didn't see the change in my Model.Firm just yet. But as the SelectedItemChanged event is only internal, I couldn't use that and that's where the databinding on SelectedItem applies the values to databound items.
Now the reason why the change isn't visible yet, is because the SelectedItemChanged is only fired after the SelectedIndexChanged is executed. So internally in the ListBox control, it probably looks like
this.SelectedIndex = value;
this.SelectedItem = FindItem(value);
this.SelectedIndexChanged(/*values*/);
this.SelectedItemChanged(/*values*/); // Apply databinding changes
So it's quite normal that you don't see the changes, before the change has occured. And I didn't know this, so I was kinda stumped why the SelectedItem (who was displaying the changed value) wasn't copied over to the databound model property.
So I didn't have to change anything major to get it all working. :)

WPF CheckBox.IsChecked binding

I have a datagrid with a variable number of columns that I am generating programatically. It contains DataGridTemplateColumns, each with a DockPanel containing a CheckBox and a TextBlock.
Binding code:
Binding bindingPicked = new Binding(string.Format("Prices[{0}].Picked", i));
bindingPicked.Mode = BindingMode.TwoWay;
CheckBox code:
FrameworkElementFactory factoryCheckBox = new FrameworkElementFactory(typeof(CheckBox));
factoryCheckBox.SetValue(CheckBox.IsCheckedProperty, bindingPicked);
Picked property:
private bool _picked;
public bool Picked
{
get { return _picked; }
set { _picked = value; }
}
When the datagrid is initialized, the Picked getters are called as expected. However, when I check/uncheck a checkbox, the setter isn't called. What is causing this? I do not want to use a DependencyProperty, and I don't think it should be needed as I just need the property setter to be called when the user clicks the CheckBox.
EDIT: Apparently I am a moron, I simply forgot
bindingPicked.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
Feel free to close this.
bindingPicked.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
That should do it :)
I Think you should Implement INotifyPropertyChanged's and call the event in set
As above, you need to implement INotifyPropertyChanged
The correct pattern to follow is:
private bool _picked;
public bool Picked
{
get { return _picked; }
set
{
if (_picked != value)
{
_picked = value;
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs("Picked"));
}
}
}
}
The UpdateSourceTrigger property tells databinding when to update the source.
For example, with a TextBox, the default is LostFocus. For most other controls it is PropertyChanged.

Categories