DataBinding RadioButton IsSelected only works once - c#

I have a problem with data binding. I am using MVVM Light.
When I set a breakpoint on the setter of one of the bools in my view model and select the corresponding radio button, the debugger halts and all looks fine.
Then when I continue, select the other radio button and select the first radio button again, the debugger is not halting. What is going wrong?
I have two two radiobuttons:
<RadioButton Margin="5" Grid.Row="1" Content="Browser cookies" GroupName="loginmethod" IsChecked="{Binding IsBrowserCookiesChecked}"/>
<RadioButton Margin="5" Grid.Row="2" Content="Username + password" GroupName="loginmethod" IsChecked="{Binding IsUsernamePasswordChecked}"/>
I have bounded them to two separate bools in my view model which looks as follows:
public const string IsUsernamePasswordCheckedPropertyName = "IsUsernamePasswordChecked";
private bool _isUsernamePasswordChecked = false;
public bool IsUsernamePasswordChecked
{
get
{
return _isUsernamePasswordChecked;
}
set
{
if (_isUsernamePasswordChecked == value)
{
return;
}
RaisePropertyChanging(IsUsernamePasswordCheckedPropertyName);
_isUsernamePasswordChecked = value;
RaisePropertyChanged(IsUsernamePasswordCheckedPropertyName);
}
}
public const string IsBrowserCookiesCheckedPropertyName = "IsBrowserCookiesChecked";
private bool _isBrowserCookiesChecked = true;
public bool IsBrowserCookiesChecked
{
get
{
return _isBrowserCookiesChecked;
}
set
{
if (_isBrowserCookiesChecked == value)
{
return;
}
RaisePropertyChanging(IsBrowserCookiesCheckedPropertyName);
_isBrowserCookiesChecked = value;
RaisePropertyChanged(IsBrowserCookiesCheckedPropertyName);
}
}

I find that when you're doing databinding on radio buttons it's a lot easier to give each button a different GroupName so they don't affect each other, then handle the setting and unsetting of each radio button choice via binding only. For example you could expose just one boolean on your viewmodel, have one radio button bind to that bool, then have the other bind to the same one, but with a inverse boolean converter. Or make the property an enum, then assign each radio button to that property, but with an enum to boolean converter. Then you have the added benefit of fewer properties to worry about on your viewmodel.
When you give radio buttons the same GroupName WPF will try to mess with the other properties every time you change one and you can get some ugly loops and weird behavior.

Related

Distinguish if the user or the program changed a value in a wpf datagrid

i have a mvvm application in which i have a DataGrid in Wpf and want to get notified if a user changes a value in a column.
All dataGridColumns have a binding to my viewmodel, which invokes a PropertyChanged Command if it gets changed. Now the Problem is, how i can determine if the property has been changed by the user or by the code? Because i only want to add a note to the corresponding line when it has been changed manually by the user.
The Column of interest is implemented like this in wpf:
<DataGridTextColumn
Header="DIL"
Binding="{Binding DilutionFactor, StringFormat={}{0:n4}}"
Visibility="{Binding Value,
Source={StaticResource DilVis},
Converter={StaticResource BooleanToVisibilityConverter}}"
IsReadOnly="False"/>
Which is bound to the ViewModel Property:
public double DilutionFactor
{
get { return _dilutionFactor; }
set
{
_dilutionFactor = value;
Update(); // PropertyChanged Command
UpdatePipetteVolumes(); // function to update corresponding volumes
}
}
Is there a event or anything i can use to trigger a method when the user changes the value of the DIL column, which is not triggered when the code updates the value?
You could set a boolean flag each time before you programmatically change the value. Then in the property setter you can check that property to see if the user invoked the change. However, this method might need a lot of code changes for heavily used properties.
Another way:
Add a second property which just sets and returns the existing property. Then use that new property for the datagrid binding:
public double DilutionFactorUser
{
get { return this.DilutionFactor; }
set
{
this.DilutionFactor = value;
// Here comes the code that is only executed on user-invoked changes
}
}
public double DilutionFactor
{
get { return _dilutionFactor; }
set
{
_dilutionFactor = value;
Update(); // PropertyChanged Command
UpdatePipetteVolumes(); // function to update corresponding volumes
}
}
Set up your Datagrid to bind to DilutionFactorUser
You could also use the DataGrid.CellEditEnding(object sender, DataGridCellEditEndingEventArgs e) event instead of looking the Property. e.Row.Item will have the data you are binding to.

WPF, detect which Tab you are navigating AWAY from

I have a WPF application with multiple tabs. Under each tab, a user can change some settings (using CheckBoxes, TextBoxes, etc) and then the user must click the "Update" button in order to save those settings. Everything works fine but one of requirements is to alert the user if he tries to switch to other tab without clicking the "Update" button.
So I'm trying to use the
TabItem_LostFocus
event handler to achieve it but this event is triggered every time I click on something within the tab. I guess I can patch this issue by placing
e.Handled = true
for every control I have but this doesn't sound like an elegant solution (especially when I don't have click event handlers for everything under my tabs). Is there some other way to determine when you are switching away from the current tab?
Thank you
To achieve your requirement, you just need to data bind to the TabControl.SelectedIndex or the TabControl.SelectedItem properties:
<TabControl ItemsSource="{Binding TabItemCollection}"
SelectedItem="{Binding SelectedTabItem}" />
Then in your view model or code behind:
private YourDataType selectedItem;
public YourDataType SelectedItem
{
get { return selectedItem; }
set
{
// selectedItem represents the previous TabItem
// value represents the new TabItem
selectedItem = value;
}
}
You can bind to the IsSelected property of each TabItem.. and then do your checking inside the setter
<TabControl>
<TabItem IsSelected="{Binding TabItem1IsSelected}"/>
</TabControl>
Property:
public bool TabItem1IsSelected
{
get { return _tabItem1IsSelected; }
set
{
if (_tabItem1IsSelected)
{
if (!value)
{
// Check to see if user has updated
if (!userUpdated)
{
value = true;
}
}
}
_tabItem1IsSelected = value;
RaisePropertyChanged();
}
}

One property for multiple buttons

Is there a way to write in c# one property for multiple items. e.g. i have 5 buttons, i don't want to write button1.text = "etc", button2.text = "etc, I want to write button.text="etc" and have button1.text through button5.text to have "etc" text.
I guess this is feasible with something similar to:
public void SetButtonText(string value) {
this.Controls.OfType<Button>().ToList().ForEach(b => b.Text = value);
}
Or the same through a property:
public string ButtonText {
set {
Controls
.OfType<Button>()
.ToList()
.ForEach(b => b.Text = value);
}
}
EDIT
After a further research, I found out that there are no direct way to access the controls of a page in Windows Phone as I know. So it all depends on whether you wish to get down from the PhoneApplicationPage:
As I see it, your solution revolves around the Page.LogicalChildren Property.
public class MyPage : Page {
public string ButtonText {
set {
LogicalChildren
.OfType<Button>()
.ToList()
.ForEach(b => b.Text = value);
}
}
}
Since the LogicalChildren has a protected accessor, you need to access it through a derived class, which shall be convenient for any kind of page you're working on Windows Phone, I guess.
Or drop a Grid right onto the PhoneApplicationPage and then drop other controls over it such as your buttons, then you shall access them through the Grid.Children property.
So, having dropped your Grid and naming it myBaseGrid, one would do the following:
public void SetButtonsText(string text) {
myBaseGrid.Children
.OfType<Button>()
.ToList()
.ForEach(b => b.Text = "myText");
}
I would personally go with the method which name makes it clear what you're doing by spelling the word Button in plural as in my sample.
Perhaps you are looking for control arrays: http://msdn.microsoft.com/en-us/library/aa289500(v=vs.71).aspx?
You can't assign all 5 buttons to the same reference, so that button.text = "etc" will work.
You can however, bind the buttons to the same property:
<Button Content="{Binding myText}"/>
<Button Content="{Binding myText}"/>
<Button Content="{Binding myText}"/>
<Button Content="{Binding myText}"/>
If the binding is set properly with INotifyPropertyChanged, then all will update when myText is updated.
You could also put the controls into a collection and foreach over them to set their Content property as others have suggested.
One way would be to create a method that sets them all for you, which you would have to manually write once:
public void SetAllButtonTexts(string text)
{
button1.text = text;
button2.text = text;
// . . .
}
Alternatively you could use a loop:
public void SetAllButtonTexts(string btnText)
{
foreach (var control in this.Controls.OfType<Button>())
{
(control).Text = btnText;
}
}
And if you don't want to update ALL the buttons, one easy but not-so-elegant thing you could do is modify the Tag property of the buttons you want to change with some custom text, and only update those:
public void SetAllButtonTexts(string btnText, string tagText = "")
{
foreach (var control in this.Controls.OfType<Button>()
.Where(b => string.IsNullOrEmpty(tagText)
|| (b.Tag != null && b.Tag.Equals(tagText))))
{
(control).Text = btnText;
}
}
In a few words: Group up all your buttons which should get changed in a list. Then later loop through this list and set your text of all buttons.
Here's some code.
First of all:
public static List<Button> buttonList = new List<Button>{};
On form_load:
buttonList.AddRange(new List<Button>{ button1,button2,button3,...}); // Group your buttons
Now it depends on 'when' or 'where' you want to change it. If the buttons should be changed right in the beginning, put the following code into the form_load-event. Else when it should be fired on an event, place it into an event.
foreach(Button btn in buttonList)
{
btn.Text = "Change all button-texts from list at one time.";
}
You can also handle multiple lables or boxes etc. like this. Just declare the right datatype.
Greetings

How to change one edit form from two buttons?

I have 2 WPF forms.
In form1, I have 2 buttons(add,edit) and in form2, 3 fields. When I click on 1 of 2 buttons open form2. How to make, when I click on button Add in second form all fields are 'IsEnabled="True"', but when I click Edit button, 1 field to be IsEnabled="False" in second form!?
10x
First, create a public bool property to bind to:
private bool canEdit = false;
public bool CanEdit
{
get { return canEdit; }
set { canEdit = value; NotifyPropertyChanged("CanEdit"); }
}
Then bind it to the IsEnabled properties of the TextBox controls:
<TextBox IsEnabled="{Binding CanEdit, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type YourXmlNamespace:YourParentForm}}}" />
Then just set it to true in either your Click handler, or your Command handler to make the TextBox controls enabled:
CanEdit = true; // Makes TextBox.IsEnabled = true
Notes:
You may prefer to add another bool property for your one control that should be enabled when the edit button is clicked. Also, you may prefer to use the IsReadOnly property instead of IsEnabled.
UPDATE >>>
I'm assuming that either your second form is launched from your first form, or they are both launched from somewhere else... in this case, you can add the property to the parent form and bind to it from the child form(s) with the RelativeSource binding that I have shown in the example.
You can bind

Using IDataErrorInfo on related but non-mutually exclusive checkboxes

I have a series of checkboxes on a form. One or more must be checked, and if not I want to display an error icon on them until one of them is.
My IDataErrorInfo implementation looks like so:
public string this[string columnName]
{
get
{
switch (columnName)
{
case "option1":
case "option2":
case "option3":
if (!this.option1 && !this.option2 && !this.option3)
return "Please select one or more of the 3 options";
}
}
}
Now, if none of the checkboxes that are bound to options1-3 are checked, each checkbox will have an error icon on them, which is fine, but when one of them IS checked, only that one checkbox will have its error icon removed (as opposed to all of them).
What's the ideal way of having the form re-poll validation for options1-3 when any one of them is changed?
If it helps (though I don't think it should be much different from normal winforms controls), I'm using DevExpress UI controls, so the checkboxes are CheckBoxEdit's and the ErrorProvider is the DxErrorProvider.
EDIT: SOLVED
I ended up manually notifying of property changed for the other options when one was changed.
private bool option1;
public bool Option1
{
get { return this.option1; }
set
{
this.option1 = value;
this.notifyPropertyChanged("Option1");
this.notifyPropertyChanged("Option2");
this.notifyPropertyChanged("Option3");
}
}
// repeat for options2-3

Categories