WPF Radio Button / MVVM Binding Issues - wpf

I've searched and discovered several proposed solutions, but I still can't make this work:
I'm using .Net 4.5, WPF, MVVM
I want to implement three radio buttons to allow a user to select which method (Primary, Secondary, Tertiary) to view. The UI, looks like this:
[ 1 ] [ 2 ] [ 3 ] (I had an image, here, but, apparently, I can't post that unless I have a 10 rep...)
My XAML:
<StackPanel Grid.Column="4" Orientation="Horizontal">
<RadioButton x:Name="rb1" Style="{StaticResource Radio1Button}" Command="{Binding Path=Select1ReceivingMethodCommand}" IsChecked="{Binding Path=ReceivingMethod1IsSelected}" />
<RadioButton x:Name="rb2" Style="{StaticResource Radio2Button}" Command="{Binding Path=Select2ReceivingMethodCommand}" IsChecked="{Binding Path=ReceivingMethod2IsSelected}" />
<RadioButton x:Name="rb3" Style="{StaticResource Radio3Button}" Command="{Binding Path=Select3ReceivingMethodCommand}" IsChecked="{Binding Path=ReceivingMethod3IsSelected}" />
</StackPanel>
Style for Radio1Button: (Others are similar, obviously)
<Style x:Key="Radio1Button" TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Image Name="Button_Image" Source="/Resources/1.Normal.png" SnapsToDevicePixels="True" Width="20" Height="20" />
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="/Resources/1.Hover.png" TargetName="Button_Image" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Source" Value="/Resources/1.Disabled.png" TargetName="Button_Image" />
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Source" Value="/Resources/1.Pressed.png" TargetName="Button_Image" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My view model exposes bool properties for the state of each radio button, as indicated in the IsChecked propery for each. I have commands defined, as indicated, as well.
For some reason, the bool properties bound to the IsChecked property on each radio button never get called. (Neither getter nor setter). There are all sorts of other controls on the page and binding is working just dandy-like for them. The UI bits seem to work fine. The proper button images are shown for the state of the button and they function mutually exclusively, as expected. But there is no interaction with my view model.
I've tried pulling out the Command bindings, wondering if they were getting in the way, but no change. I've tried using converters, but the converter was never called.
I would also like a button to be disabled if there is no method assigned. (i.e. If only Primary and Secondary methods are defined, then the '3' radio button should be disabled. I was hoping the Command binding would help with that.)
What am I missing? What is the best way to implement this? (A controls other than radio buttons?)

it works on my computer, maybe you need to check your DataSource or see the output window from visual studio.

Never mind... There was a legacy DataContext assignment getting in the way. Once I cleaned that up, it worked as expected.
Sorry to bother... As you were.. Nothing to see here...

Related

In a MVVM WPF app how can I show a validation error for a button?

I have a view which is bound to a view model having a number of properties. Some of the properties are bound directly to controls like text boxes. For these I set a validation template which shows a red border with corner arrow which shows the errors for the property in a tool tip.
Other properties are set by forms which are opened in response to clicking buttons. For example I have button labeled "Click to edit duration" which pops up form to edit the StartDate and EndDate properties. The button is bound to a command in the view model which pops up the form.
The view model implements INotifyDataErrorInfo so for the controls which are bound directly to properties I can just attach error messages to the properties they are bound to and raise the ErrorsChanged event.
What I want is if the start and end dates are not properly set then a red border and tool tip is applied to the button. Attaching errors to StartDate and EndDate properties won't work because the button is not bound to these properties.
I had done the same thing in an application where there is an additional section of data that needs to be completed to continue. The way I was able to make the button have a validation error was by using a Tag like this:
<Button Grid.Column="1" Grid.Row="0" Content="{DynamicResource resEnterSecureInformation}" Width="200" Command="{Binding PrimaryApplicant.SecureInformation.OpenSecureInformationWindowCommand}" Tag="{Binding PrimaryApplicant.SecureInformationComplete, ValidatesOnDataErrors=True}"></Button>
I also had a style in my app.xaml to add the red outline and a tooltip when an error was set:
<Style TargetType="Button">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)/ErrorContent}"></Setter>
<Style.Triggers>
<Trigger Property="IsVisible" Value="True">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="1" >
<AdornedElementPlaceholder/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<AdornedElementPlaceholder/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
I did this to also not show the validation error when the button was disabled (no need showing the user something they can't go into and fix)
Hope this helps

ElementName binding doesn't work for IsMouseOver in ControlTemplate?

I have a control template and I want to trigger some actions only if mouse is over a certain part of it. Here is the core of my template (simplified for demonstration):
<ControlTemplate TargetType="{x:Type graphicElements:MyTabItem}">
<Grid x:Name="templateRoot">
<Grid x:Name="templateChild" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, ElementName=templateChild}" Value="True">
<Setter Property="Background" TargetName="templateRoot" Value="Red" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
When I put ElementName as templateRoot it works and turns red. When I put it as templateChild it doesn't work... Why not?
In the simplified version of your code the binding to the templateRoot grid also won't work. The problem is, that WPF needs to perform Hit Tests on the elements to raise certain events or to update the IsMouseOver property. Since you don't have a background brush set for the grids, they will never receive mouse inputs, hence your trigger will never execute. Try this:
<Grid x:Name="templateRoot">
<Grid x:Name="templateChild" Background="Transparent"/>
</Grid>

focus is wrong when trying to click inside a RadComboBox

So i have a RadCombobBox which is heavilly edited to have a list of options where 1 of the options can be an input field. There are different types of input fields you can chose from but i'm having a problem with the integer input field. There's also one with a text input field which shows no problems.
The combobox looks like this:
The text is dutch (don't mind it) beneath the text options there is a preset value, the idea here is you can either choose a preset setting (which corresponds to an integer value) or you can type in your own value.
What happens when i try to click on different places (last one is very peculiar):
If i try to click on the input box (which is highlighted in gray) it selects it and closes the combo box (this is not what i want).
If i try to click on the arrows on the right side of the combobox it changes the value.
If i scroll with my mouse, it changes the value.
If i keep my mouse down on the input box i can actually type in the value i want.
If i first click on the buttons on the side and then click inside the input box i can edit the value.
What I want is to click on the input box and be able to edit the value inside and when i'm finished press enter (or outside the combobox) to close it.
Somehow the focus or, something (i am not 100% sure) just seems to fail me.
here is the xaml code for the IntegerDataTemplate:
Integer selector
<Style x:Key="NumericUpDownStyle" TargetType="{x:Type telerik:RadNumericUpDown}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="BorderBrush" Value="{StaticResource AccentBrush}" />
</Trigger>
<Trigger Property="IsEditable" Value="False">
<Setter Property="SmallChange" Value="0" />
<Setter Property="LargeChange" Value="0" />
</Trigger>
</Style.Triggers>
</Style>
<!-- Integer editor -->
<DataTemplate x:Key="IntegerDataTemplate">
<telerik:RadNumericUpDown x:Name="NumericUpDown"
Width="{Binding Path=ActualWidth,
ElementName=Editor}"
MaxWidth="{Binding Path=ActualWidth,
ElementName=Editor,
Converter={StaticResource WidthToWidthConverter}}"
HorizontalContentAlignment="Left"
Background="Transparent"
FontFamily="{telerik:Windows8Resource ResourceKey=FontFamilyStrong}"
IsInteger="True"
Style="{StaticResource NumericUpDownStyle}"
UpdateValueEvent="PropertyChanged"
Value="{Binding Path=Value,
UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True}">
<telerik:RadNumericUpDown.NumberFormatInfo>
<globalization:NumberFormatInfo NumberGroupSeparator="" />
</telerik:RadNumericUpDown.NumberFormatInfo>
</telerik:RadNumericUpDown>
</DataTemplate>
<!-- Integer as Option -->
<DataTemplate x:Key="OptionsDataTemplate">
<TextBlock Height="20" Text="{Binding Converter={StaticResource IntegerSelectorObjectToStringConverter}}" />
</DataTemplate>
<local:SelectorTypeTemplateSelector x:Key="IntegerTemplateSelector"
OptionsDataTemplate="{StaticResource OptionsDataTemplate}"
SelectorDataTemplate="{StaticResource IntegerDataTemplate}" />
Somewhere it is going wrong, is there anyone that can point me to the right way to fix it (a work around would be fine as well).
I would like to add I have the same code but with a text box which does work properly, I've added the code below to have a comparison but i haven't been able to find a significant difference.
Text selector (which actually works properly)
<Style x:Key="TextBoxStyle" TargetType="{x:Type telerik:RadWatermarkTextBox}">
<Setter Property="BorderBrush" Value="{StaticResource BasicBrush}" />
<Setter Property="FontFamily" Value="{telerik:Windows8Resource ResourceKey=FontFamilyStrong}" />
<Setter Property="Padding" Value="2,2,0,0" />
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ErrorTemplate}" />
<Setter Property="telerik:RadWatermarkTextBox.WatermarkTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Margin="2,3,0,0"
FontFamily="Segoe UI"
FontStyle="Italic"
Foreground="{StaticResource WaterMarkBrushNoOpacity}"
Padding="0,-2,0,0"
Text="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="BorderBrush" Value="{StaticResource AccentBrush}" />
</Trigger>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource MarkerDisabledBrush}" />
</Trigger>
</Style.Triggers>
</Style>
<!-- String editor -->
<DataTemplate x:Key="StringDataTemplate">
<telerik:RadWatermarkTextBox x:Name="WatermarkTextBox"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Top"
Background="Transparent"
BorderThickness="1"
Style="{StaticResource TextBoxStyle}"
Text="{Binding Path=Value,
UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True}" />
</DataTemplate>
<!-- String as Option -->
<DataTemplate x:Key="OptionsDataTemplate">
<TextBlock Height="20" Text="{Binding Converter={StaticResource StringSelectorObjectToStringConverter}}" />
</DataTemplate>
<local:SelectorTypeTemplateSelector x:Key="StringTemplateSelector"
OptionsDataTemplate="{StaticResource OptionsDataTemplate}"
SelectorDataTemplate="{StaticResource StringDataTemplate}" />
It's really difficult to know what's going on without actually fiddling around with all the bits and pieces (e.g. the RadComboBox style + code), but it would be natural to assume that is has something to do with the drop down automatically closing when it loses focus, especially given that the standard WPF ComboBox has a FocusedDropDown state which opens the popup.
You could try and duplicate the RadComboBox by importing the style from Telerik, and extending the code behind into a new class (and restyle with the imported template). This way you can override methods and attach to events (e.g. Got/LostFocus), and play around with the template to see if you can adjust it to your needs.
However trying to wrestle such behavior into existing controls, just because it is possible to restyle the templates, often just ends up in a lot of grief (and hours wasted).
I have a feeling it would be easier to create a NumericRadComboBox which has the numeric up/down embedded in the combobox itself. So you restyle the RadComboBox to have numeric up/down buttons next to the drop down arrow, and implement the increment/decrement behavior manually.
The solution was rather simple, I was trying to fix it with preview mouse down but what i needed to do was on preview mouse up by adding: PreviewMouseLeftButtonUp="EditorPreviewMouseLeftButtonUp" in the xaml, which made the RadComboBox code as follows:
telerik:RadComboBox x:Name="Editor"
BorderThickness="0"
FontWeight="SemiBold"
ItemTemplateSelector="{StaticResource IntegerTemplateSelector}"
PreviewMouseLeftButtonUp="EditorPreviewMouseLeftButtonUp"
SelectionBoxTemplate="{StaticResource SelectionboxTemplate}"
Validation.ErrorTemplate="{StaticResource ErrorTemplate}" />
The code behind looked lik this:
private void EditorPreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var originalSource = e.OriginalSource as FrameworkElement;
if ((originalSource != null) && (originalSource.ParentOfType<TextBox>() != null))
{
e.Handled = true;
}
}
The MouseUp is when the selection changed event is called which causes the combobox to close, by stating the event is finished before that happens it won't close the combobox. All you now have to do is press enter after you've changed the value and it will be selected and updated correctly.

Trying to add a trigger to a button to change the button's Content property

I've got a UserControl that has a button on it. The UserControl has a DependencyProperty called IsNew. This is a bool value that is set to true if the object being edited in the control was newly created and hasn't been written to the database yet. It is false otherwise.
I've got a button that currently reads "Save". I've been asked to change the button so it reads "Insert" if the row is new. I now have the XAML below for the button:
<Button Background="{DynamicResource ButtonBackground}"
Click="SaveButton_Click"
Content="Save"
FontSize="20"
FontWeight="Bold"
Foreground="{DynamicResource ButtonForeground}"
Height="60"
IsEnabled="{Binding Path=IsDirty, RelativeSource={RelativeSource AncestorType={x:Type cs:Editor}}}"
Margin="5"
Name="SaveButton"
TabIndex="7"
Visibility="{Binding Path=CanModify, Converter={StaticResource BoolToVisibility}}">
<Button.Triggers>
<Trigger Property="Editor.IsNew" Value="True">
<Setter Property="Button.Content" Value="Insert" />
</Trigger>
<Trigger Property="Editor.IsNew>
<Setter Property="Button.Content" Value="Save" />
</Trigger>
</Button.Triggers>
</Button>
I'm getting an exception at run time, whose inner exception reads:
Triggers collection members must be of type EventTrigger.
How do I get this to work? I could do this easily in the code-behind, but I want to do it in the XAML.
Thanks
Tony
You can use only EventTriggers in Control.Triggers. To use other kind of trigger (Trigger, DataTrigger) you should use style:
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="Editor.IsNew" Value="True">
<Setter Property="Button.Content" Value="Insert" />
</Trigger>
And also your Property="Editor.IsNew" and Property="Button.Content" won't work because triggers belongs to Button and trigger will try to find property "Editor.IsNew" on button. You can fix it by changing trigger to DataTrigger and bind with RelativeSource to your UserControl and its IsNew property. And Property="Button.Content" needs to be changed to Property="Content".
The reason for this is that a control can only implement EventTriggers. The other kind of triggers you need have to be implemented in a style, and then your button uses that style.

WPF DataGrid Hyperlink Appearance and Behaviour

I am quite new to WPF, there is so much to learn and I think I'm getting there, slowly. I have a DataGrid which is used for display and user input, it's quite a tricky Grid as it is the main focus of the entire application. I have some columns that are read-only and I have used a CellStyle Setter to set KeyboardNavigation.IsTabStop to False to keep user input focused on the important columns and that works fine. I would like a couple of the read-only columns to be hyperlinks that show a tooltip and do not receive the focus, however I am struggling to write the XAML that will achieve all three requirements at the same time.
One of the columns is to indicate if the item on the row has any Notes. I have used the following XAML to display a HasNotes property in the cell in a DataGridTemplateColumn and on the Tooltip show the actual notes, in the Notes property:
<DataGridTemplateColumn x:Name="NotesColumn" Header="Notes">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding HasNotes, Mode=OneWay}">
<TextBlock.ToolTip>
<TextBlock Text="{Binding Notes}" MaxWidth="300" TextWrapping="Wrap" />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style>
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
That works fine but I would like to make it a Hyperlink instead so the user can do something with the Notes when they click on the cell contents.
I have another column which is a DataGridHyperlinkColumn used to display a Unit of Measurement on a hyperlink and when clicked the user can change the unit. (The reason I have done it like this, rather than a ComboBox for example, is because as much as I want the user to be able to change the unit I want to make the interface such that it is a very deliberate act to change the unit, not something that can be done accidentally). The following XAML puts the Hyperlink column on for Unit
<DataGridHyperlinkColumn x:Name="ResultUnitLink" Binding="{Binding Path=Unit.Code}" Header="Unit" Width="Auto" IsReadOnly="True">
<DataGridHyperlinkColumn.CellStyle>
<Style>
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
</Style>
</DataGridHyperlinkColumn.CellStyle>
<DataGridHyperlinkColumn.ElementStyle>
<Style>
<EventSetter Event="Hyperlink.Click" Handler="ChangeUnit" />
</Style>
</DataGridHyperlinkColumn.ElementStyle>
</DataGridHyperlinkColumn>
One problem with the XAML for the Hyperlink column is that the IsTabStop = False doesn't appear to work, when tabbing through the Grid my hyperlink column still receives the focus, unlike the other columns where I've used a setter to change IsTabStop. If push comes to shove I could live with that but I'd rather not.
What I actually want from both those columns is an amalgamation of the two appearances/behaviours i.e. Columns where the data is displayed on a hyperlink, where TabStop = False and which display a tooltip of a different property when hovered over.
Can anyone help advise me how to get a column that achieves the following:
Hyperlink displaying one property
Tooltip displaying a different property
IsTabStop = False that actually works when used with a hyperlink
Thanks in advance to anyone who can help.
I've had issues with the Hyperlink in the past, so have this Style which I use for Labels or Buttons to make them look like Hyperlinks. Try making your column Template into a Button or a Label and applying this style.
<Style x:Key="ButtonAsLinkStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ContentPresenter ContentStringFormat="{TemplateBinding ContentStringFormat}" />
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Blue" />
<Setter Property="Cursor" Value="Hand" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>

Resources