Is it possible to use trigger setter to change ViewModel property? - c#

I am opening Popup using IsOpen bound to some hard to reach attached property. All I want is to somehow pass IsOpen value to ViewModel.
Trying to use setter for this:
<Popup StaysOpen="False"
IsOpen="{Binding Path=(local:ListViewBehavior.IsColumnHeaderClicked), RelativeSource={RelativeSource FindAncestor, AncestorType=GridViewColumnHeader}}">
<Popup.Style>
<Style TargetType="Popup">
<Style.Triggers>
<Trigger Property="IsOpen" Value="True">
<!-- this is not possible -->
<Setter Property="{Binding IsPopupOpened ...}" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</Popup.Style>
</Popup>
Gives:
A 'Binding' cannot be set on the 'Property' property of type 'Setter'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
Is there a way to pass IsOpen value (which is already bound to something else in the View) to ViewModel?

You could created an attached Property, which is set by eventhandlers to the Opened and Closed Events of the Popup. That can be bind to the IsPopupOpen Property of your ViewModel by OneWayToSource Binding.
I'm also not quite sure if there is an easier Solution, but this is a kind of "Workaround" what I would do, in case that no-one provides a better solution here.

Related

How to update a DataTemplate dynamically based on a object property?

I have a collection of objects that have different rendering options: They can be simple text, editable text, comboboxes, or event a mixed bag (like a comboBox where items are usually text but with images for specific values).
I managed to show everything correctly by using ContentTemplateSelector inside a ContentPresenter node inside the DataTemplate of the ListViewItem.ItemTemplate:
<ContentPresenter
Grid.Row="1"
Grid.Column="1"
Content="{Binding .}"
ContentTemplateSelector="{DynamicResource ResourceKey=RenderTemplateSelector}"/>
(note that everything is inside the DataTemplate of a ListView.ItemTemplate)
All is good until I change the value of said property and need to change the template, for instance going from a image to a text.
The VM does update correctly the values, but nothing happens in the GUI.
Looking around here there are a few methods (using a Converter (bound to which property??), defining a Style (i think is not relevant, since i must show different controls, not change properties of the same one), using an AttachedProperty (I don 't understand really well how attached properties work, but from what I saw I should have a ContentControl node, which I don't...) but nothing seems what I need.
So, a recap: I have a ListView that has a DataTemplate definded for each ListViewItem that contains another DataTemplate that needs to change based on a properti of the ListViewItem object. I managed to achieve this with a ContentTemplateSelector, but that is assigned at the beginning and then never changed.
You could try to replace the ContentPresenter with a ContentControl that has a Style with data triggers that sets the ContentTemplate, .e.g.:
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding YourProperty}" Value="Type1">
<Setter Property="ContentTemplate" Value="{StaticResource template1}" />
</DataTrigger>
<DataTrigger Binding="{Binding YourProperty}" Value="Type2">
<Setter Property="ContentTemplate" Value="{StaticResource template2}" />
</DataTrigger>
<!-- ... -->
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>

Validating UserControl's control by Window's ViewModel

Suppose, I have created custom LoginForm as an UserControl, which consists of TextBox and PasswordBox. TextBox has installed custom ErrorTemplate. Naturally, I would like to have this LoginForm as reusable as can, therefore I want to separate validation logic from this LoginForm.
The problem is, that if I bind LoginForm's text property to the "validation-property" of the ViewModel : IDataErrorInfo, that is set as Window's DataContext, the ErrorTemplate is not being applied to LoginForm's TextBox even if I see debug logs from ViewModel's validator.
How can I validate child controls of reusable component via independent ViewModel?
use this error template in application resource:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="0.8">
<AdornedElementPlaceholder Name="adornerPlaceholder"></AdornedElementPlaceholder>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
After hours of struggling how to solve this, I came up with following solution which satisfies my requirements and so MVVM pattern:
Create in UserControl a DepdendencyProperty of type IDataErrorInfo which will be later implemented by your ViewModel (for my purposes, I used ISignUpValidator:IDataErrorInfo with UsernameValue property).
Lets say this property is registered under the name Validator (as default value I used "do-nothing" implementation of my interface).
Bind UserControl's TextBox.Text property to Validator property:
Lets say TextBox.Text property is exposed to UserControl under the name
Username:
Username="{Binding Path=Validator.UsernameValue, ElementName=UserControlName,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}"
Finally Bind your Viewmodel to your UserControl's Validator property
<Window.Resources>
<local:ViewModel x:Key="ViewModel"/>
<Window.Resources>
<local:LoginForm Validator={StaticResource ViewModel}>
Or alternatively, if your ViewModel is already set as Window's DataContext:
<local:LoginForm Validator="{Binding DataContext, ElementName=WindowName}"}>

Hide controls, if their commands shall not be executed

There is an application, which has different permission modes. Depending on the mode, the application has a limited functionality.
Therefore I created my own RestrictedRoutedUICommand class, which inherits from RoutedUICommand and it knows, when it (the command) is allowed to be executed.
This class RestrictedRoutedUICommand has a property Admission, which indicates, if it is allowed to execute the command and an event OnAdmissionChanged which fires, when this property becomes changed.
The Question is: How can I tell those controls, which do have forbidden commands, to hide, if their command is forbidden?
It should be similar to the functionality of controls turning disabled, if their command can not be executed.
Edit: I don't want to use the RoutedUICommand.CanExecute(). Because CanExecute() should only determine, if it is possible to execute the command. So I added another method to the RoutedUICommand which determines if the command is allowed, I want the controls to use this one.
Since Button already becomes disabled you can make Visibility dependant on IsEnabled property
<Button ...>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
or you could make use of BooleanToVisibilityConverter to do the same instead of using Trigger
<Button ... Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Converter={StaticResource BooleanToVisibilityConverter}}">
EDIT
If these properties are independent then you still can do it via Binding and Converter
<Button ... Visibility="{Binding RelativeSource={RelativeSource Self}, Path=Command.Admission, FallbackValue=Visible, Converter={StaticResource BooleanToVisibilityConverter}}"
this code will bind Visiblity to Command.Admission via BooleanToVisibilityConverter converter and if the value cannot be retrieved, because it's not RestrictedRoutedUICommand or Command is not assigned, then FallbackValue will be use

Make a binding on ElementName to change when trigered

I'm probably missing something because what I do is suposed to work I think. I'm trying to bind the ElementName of a value to change when trigered.
here's the code (it is in the style):
<ContentPresenter>
<ContentPresenter.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Tick}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName={Binding FieldNameFocus}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
And get this error:
Error 2 A 'Binding' cannot be set on the 'ElementName' property of type 'Binding'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject. C:\Users\xavier\Documents\Visual Studio 2012\Projects\Test_Validation\Test_Validation\MainWindow.xaml 1 2 Test_Validation
Maybe I'm not in the right path though... I just want to have focusedElement to change according to FieldNameFocus when Tick becomes True. (Both are in my dataContext)
Thanks in advance.
Your binding should look like this: Value="{Binding ElementName=YourElementName, Path=PropertyOfTheElement}"
Example binding by ElementName:
<TextBox Name="tbx1" Text="TextBox"/>
<TextBox Name="tbx2" Text="{Binding ElementName=tbx1, Path=Text}"/>
Hello the error is pretty clear, you cannot use Binding on with ElementName. When using ElementName you should provide the name of a control present in your xaml File as the previous post showed. As your trigger is bind to a boolean value you might want to replace "ElementName={Binding FieldNameFocus}" by
"ElementName=controlNameOnWhichYouWantToSetFocus".
Eg <Textbox x:Name="ControlIWantFocusOnWhenTickIstrue"> then in your trigger
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName="ControlIWantFocusOnWhenTickIstrue"/>

Binding Button Tooltip with Content / Label

I have this code:
<ribbon:Button Label="Hello" />
I wanted to bind its tooltip to Label like:
<ribbon:Button Label="Hello" ToolTip="Hello" />
I have already tried creating style with the following info, but failed:
<Style TargetType="{x:Type ribbon:Button}">
<Setter Property="ToolTip" Value="{Binding Text}" />
</Style>
Please tell me how can I fix this. Instead of ribbon:Button, normal button code be used and I want to bind its tooltip property with its Content property.
Edit: One more thing after trying, if i set the Value property of Setter inside style without any binding, it works fine. Eg:
<Style TargetType="{x:Type ribbon:Button}">
<Setter Property="ToolTip" Value="This will show" />
</Style>
But binding is not applied here. So problem arises only when binding is done :(
Of course your style won't work, because you always bind to the properties of your DataContext, which i doubt is your Control itself.
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Text}"/>
should work fine.
The golden rule for binding is: look for binding errors in the debug output.
Secondly, you are binding Text, where everywhere else you use the property label.
Thirdly, understand that the basis for Binding is the DataContext. You can give Xaml elements a name, then use the Binding syntax based on ElementName to bind to FrameworkElement properties.

Categories

Resources