I'm not really sure how to word this, so here goes.
I've got some radio buttons on a form in my WPF application which get validated. They're basically a required field for this form.
When I start to fill in the form, both radio buttons are marked with a red border, however when I select one of the buttons. This happens:
How do I go about fixing this? I know I could change the control to something like a ComboBox or something, but typically gender fields on an electronic form are radio buttons.
I've tried getting the validation to display on the stackpanel containing the radio buttons, it's done nothing.
I can't really set a default value for the gender either incase it gets overlooked and causes a complaint/misunderstanding further down the line.
The XAML for the image above is here:
<StackPanel Grid.Column="1" Grid.Row="3" Orientation="Horizontal">
<RadioButton GroupName="Gender" Content="Male" IsChecked="{Binding Gender, Converter={StaticResource GenderToCharConverter}, ConverterParameter=M}"/>
<RadioButton GroupName="Gender" Content="Female" IsChecked="{Binding Gender, Converter={StaticResource GenderToCharConverter}, ConverterParameter=F}"/>
</StackPanel>
Edit: To clarify a bit more on what my problem is, when I open a new instance of this form, both radio buttons are marked with validation borders as they should be as Gender is a required field on this form.
When a gender is selected, the validation border is only removed from the radio button that is clicked when really I would imagine that both borders should be removed as the data-bound property now has a value.
The only way it seems to remove this border is to click the other option to remove and then re-select the original value. This shouldn't happen on this form as this could confuse the users and have them mistakingly think that there's still a problem with the gender controls, and furthermore it just looks like a bug.
Edit 2: This question fixes the border showing, but then hides the fact that one of the radio buttons selected is required. Is there not some way to put a border around the StackPanel instead rather than the individual radio buttons. It seems there is absolutely no examples on how to achieve this on Google, which is completely stupid.
Edit 3: Relevant code.
// Gender property in the viewmodel.
[Required(AllowEmptyStrings=false, ErrorMessage="Gender is a required field.")]
public string Gender
{
get { return _currentMember.Gender; }
set
{
if(value != _currentMember.Gender)
{
_currentMember.Gender = value;
RaisePropertyChanged();
Validate(_currentMember.Gender);
}
}
}
// Converter
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string input = (string)value;
string test = (string)parameter;
return input == test;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || !(value is bool))
return string.Empty;
if (parameter == null || !(parameter is string))
return string.Empty;
if ((bool)value)
return parameter.ToString();
else
return string.Empty;
}
In my opinion you have 2 solutions: the first one - the easiest one - is to set a default for Gender property. In this way one among the two radiobuttons will be check and the "required" attribute will be always satisfied.
The second solution is to "mask" a ListView as a RadioButtonGroup. For my example I used a very intuitive framework to perform validation by using attributes. You can find it here.
So, instead of using a stackpanel, I wrote this piece of XAML:
<ListView SelectedValue="{Binding Gender, ValidatesOnDataErrors=True}"
SelectedValuePath="Tag" BorderThickness="0" Margin="2" SelectionMode="Single">
<RadioButton GroupName="Gender" Margin="2" Tag="M" Content="Male" IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListViewItem}, Path=IsSelected}" />
<RadioButton GroupName="Gender" Margin="2" Tag="F" Content="Female" IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListViewItem}, Path=IsSelected}" />
</ListView>
Of course the ListView can be improved with a more suitable style (which for example hides the selection style). Since just the SelectedValue property is binded to the Gender property, you will see just one validation template around the ListView.
I hope this is the result you were looking for.
Moreover - with the that framework - I could not reproduce your issue, since if I use your stackpanel and I check one radiobutton, both the red rectangles will disappear.
Here's how to do this without the framework in the other answer (which is nice but not essential).
The key is to surround the radio buttons with another element, which has a binding to the same property. This will display the error. I set a binding for the Tag property of a StackPanel, which is otherwise not used.
Then you need to turn off the validation error template for each radio button, using 'Validation.ErrorTemplate="{x:Null}"'.
<StackPanel Tag="{Binding Gender}" >
<RadioButton IsChecked="{Binding Gender,
Converter={StaticResource GenderConverter},
ConverterParameter={x:Static enumerations:Male}}"
Content="Male" Validation.ErrorTemplate="{x:Null}"/>
<RadioButton IsChecked="{Binding Gender,
Converter={StaticResource GenderConverter},
ConverterParameter={x:Static enumerations:Female}}"
Content="Female" Validation.ErrorTemplate="{x:Null}"/>
</StackPanel>
Related
I have some radio buttons. The fist one is selected by default.
I need to disable them when processing some data.
Issue: When re-enabling them, the first one is not selected anymore, ie. none selected!
<RadioButton GroupName="ShowSelector" Content="For Selected"
IsChecked="{Binding Path=ShowForSelected, Mode=TwoWay, FallbackValue=True}"
IsEnabled="{Binding NotUnderProcessing}"/>
<RadioButton GroupName="ShowSelector" Content="For All"
IsEnabled="{Binding NotUnderProcessing}"/>
I assume that you have a ViewModel behind your View. When you process your data you have to set the NotUnderProcessing Property and bind your WrapPanel like this:
<RadioButton IsEnabled="{Binding NotUnderProcessing, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
And your ShowForSelected Property might also have the UpdateSourceTrigger in the IsChecked expression.
Edit: When I tried this I run into trouble, I fixed this by making the processing to a Task. Because the ProcessingValue was not setted. And in addition to that I added a Second Property for the second RadioButton and now it's working.
<GroupBox x:Name="groupBox" Header="Operating System" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="74" Width="280">
<StackPanel>
<RadioButton GroupName="Os" Content="Windows 7 (64-bit)" IsChecked="True"/>
<RadioButton GroupName="Os" Content="Windows 7 (32-bit)" />
</StackPanel>
</GroupBox>
I have several radio button groups in my application
How can I access which one has been checked in the Code-Behind using C#?
Is it absolutely necessary to use x:Name= on each RadioButton or is there a better way?
Code samples always appreciated
Yes! There is a better way, its called binding. Outside of binding, you are pretty much stuck (I can imagine handling all the checked events separately, and assigning to an enum, but is that really better?)
For radio buttons, you would typically use an enum to represent all the possible values:
public enum OsTypes
{
Windows7_32,
Windows7_64
}
And then bind each of your radio buttons to a global "selected" property on your VM. You need a ValueEqualsConverter for this:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((bool)value) ? parameter : Binding.DoNothing;
}
And then your radio buttons look like:
<RadioButton Content="Windows 7 32-bit"
IsChecked= "{Binding CurrentOs,
Converter={StaticResource ValueEqualsConverter},
ConverterParameter={x:Static local:OsTypes.Windows7_32}}"
Of course, you have a property in your VM:
public OsTypes CurrentOs {get; set;}
No x:Name, complicated switch statements, or anything else. Nice, clean, and well designed. MVVM works with WPF, use it!
I have a ListView control that contains one column with checkboxes only. Is it possible to give those checkboxes some names (indexes - like 1, 2, 3...)?
I need it because I want to identify a concrete checkbox in ToggleButton_OnCheckedUnchecked event in some way.
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Value, Mode=OneWay}" IsThreeState="False"
Checked="ToggleButton_OnCheckedUnchecked"
Unchecked="ToggleButton_OnCheckedUnchecked"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
Don't do it that way. Instead, have each line in your ListView have an IsChecked property, and change the IsChecked Binding to TwoWay. That way, you don't need to use the Checked and UnChecked events at all.
public class LineViewModel
{
public bool IsChecked
{
get { return _isChecked;
}
set
{
// do something here
}
}
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" IsThreeState="False"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
For best results, implement INotifyPropertyChanged too.
Probably dynamic binding some names can be done through the Binding, like that:
<CheckBox Name="{Binding IdCheckBox}" ... />
Quote from the MSDN:
Data binding a Name is technically possible, but is an extremely uncommon scenario because a data-bound Name cannot serve the main intended purpose of the property: to provide an identifier connection point for code-behind.
In short, the Binding Name property for control is impossible and undesirable. But you can use the attached dependency property, like this:
<CheckBox local:GiveName.Name="{Binding Template_Name1}" ... />
In any case, this is not the solution to your problems.
I have a grid of CheckBoxes in a WPF C# project. Each CheckBox's Command property is bound to a CheckBoxChangedCommand in my WView.xaml file, like so:
<CheckBox Grid.Row="0" IsChecked="true" x:Name ="CheckBox0"
Command="{Binding CheckBoxChangedCommand}" />
<CheckBox Grid.Row="1" IsChecked="true" x:Name="CheckBox1"
Command="{Binding CheckBoxChangedCommand}" />
Each time one of the CheckBoxes is either checked or unchecked, I call CheckBoxChanged. How would I go about displaying a pop-up window showing either 1. the row number in the grid of the CheckBox and the name of the CheckBox ("CheckBox0", for example) and 2. The Checked value (true/false) for the checkbox?
My CheckBoxChangedCommand, in WViewModel.cs file, looks like this:
public ICommand CheckBoxChangedCommand
{
get
{
return new RelayCommand(param =>
{
MessageBox.Show("CheckBoxChanged!");
});
}
}
How can I access the IsChecked property and the row number of the CheckBox that triggered CheckBoxChanged from withinCheckBoxChanged? How can I pass the data from my View to my ViewModel?
You definitely need to do more with binding here.
First of all, you should probably be binding the IsChecked property of your Checkboxes to a property on your viewmodel.
Second, based on your comment about needing to know the row number of the checkbox that was checked, I'd say you probably need to be generating the "row" including the CheckBox via databinding, so that you can then pass the object that represents the row as the CommandParameter to your CheckBoxChangedCommand.
So something like:
<ListBox ItemsSource="{Binding MyItems}" />
and then in your resources:
<DataTemplate DataType="{x:Type local:MyItemType}">
<CheckBox IsChecked="{Binding IsChecked}"
Command="{Binding CheckChangedCommand}"
CommandParameter="{Binding}" />
</DataTemplate>
Note: Your CheckChangedCommand is probably on the main ViewModel, not the item-level ViewModel, so you probably need to do some other type of lookup to make it find it - this example is just for simplicity
I'm trying to add some default text to combo boxes that will show when there is no item selected. I'm using a style to acheive this which works great when the combo is first loaded.
<Style TargetType="{x:Type igRibbon:ComboEditorTool}" x:Key="PleaseSelect">
<Style.Triggers>
<Trigger Property="SelectedIndex" Value="-1">
<Setter Property="Text" Value="Please Select" />
</Trigger>
</Style.Triggers>
</Style>
<igRibbon:ComboEditorTool Style="{StaticResource PleaseSelect}"
ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem }" />
But when the combo's selected item is reset (by setting it to null, which sets the SelectedIndex to -1) it fails to display the default text (even though the the trigger does fire), what could be the reason for this? Is there a better way to reset the selected item?
Cheers
Here is the solution that I used, thanks to #AlexPaven for the idea:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
{
return "Please Select";
}
else
{
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string && ((string)value) == "Please Select")
{
return null;
}
else
{
return value;
}
}
I'm not familiar with the Infragistics suite, but I suspect it's the same thing as with regular combo boxes: since you have a binding on SelectedItem, the Text cannot be set to an item that blatantly disobeys that binding; the Text IS a representation of the SelectedItem. If the SelectedItem is null, then the Text must also be a representation of null.
I'm guessing (didn't try it and I may be just plain wrong) you could probably accomplish this with an IValueConverter that returns a custom string when the passed object is null (and returns the object unchanged otherwise), set on the SelectedItem binding.
It's been a while since this question was asked, but let me answer it with a more Infragistics related answer.
Let's start with a short side node:
We have to be careful when using XamComboEditor without specifying the namespace,
because in the Infragistics Framework the class is defined two times.
1. Infragistics.Windows.Editors.XamComboEditor
2. Infragistics.Controls.Editors.XamComboEditor
Referring to the Infragistics Help, the recommended one is the Infragistics.Windows.Editors.XamComboEditor
See the About xamComboEditor:
We recommend that you use the xamComboEditor control instead of the xamComboEditor (Input) control. The xamComboEditor (Input) is being planned for retirement over the next few years and will not receive any new features.
And now to your question:
Both, the Infragistics.Windows.Editors.XamComboEditor and the derived Infragistics.Windows.Ribbon.ComboEditorTool, have a property for setting a default text for null values. This property is called:
NullText
The text to display when the value of the editor is null and the editor is not in edit mode. The default value is empty string. (Inherited from Infragistics.Windows.Editors.TextEditorBase)
Also the Infragistics.Controls.Editors.XamComboEditor provides such a property. It is called:
EmptyText
Gets/Sets the Text that should be displayed when the editor doesn't have anything selected. (Inherited from Infragistics.Controls.Editors.ComboEditorBase)
Example for ComboEditorTool:
Referencing the following dlls:
InfragisticsWPF4.Editors.v18.1
InfragisticsWPF4.Ribbon.v18.1
InfragisticsWPF4.v18.1
xaml-Snippet:
xmlns:ribbon="http://infragistics.com/Ribbon"
...
<ribbon:ComboEditorTool Id="SampleComboEditorTool"
NullText="Select ..."
ItemsSource="{Binding }"
/>
Screenshot:
Example for Infragistics.Windows.Editors.XamComboEditor:
Referencing the following dlls:
InfragisticsWPF4.Editors.v18.1
InfragisticsWPF4.v18.1
xaml-Snippet:
xmlns:editors="http://infragistics.com/Editors"
...
<editors:XamComboEditor Width="120" Height="23"
ItemsSource="{Binding}"
NullText="Select ..."
/>
Screenshot:
Example for Infragistics.Controls.Editors.XamComboEditor:
Referencing the following dlls:
InfragisticsWPF4.Controls.Editors.XamComboEditor.v18.1
InfragisticsWPF4.v18.1
xaml-Snippet:
xmlns:ig="http://schemas.infragistics.com/xaml"
...
<ig:XamComboEditor Width="120" Height="23"
ItemsSource="{Binding}"
EmptyText="Select ..."
/>
Screenshot:
Second side note: the first occurrence I've found for the property NullText was in the Help Doc of Version 2012.1. See here