Change Binding Format with Focus - wpf

WPF application using MVVM. I have a TextBox whose Text property is bound to a decimal property of the view model. This property represents a currency value. I would like the TextBox to display the data in currency format. To this end, I have set StringFormat to "c" in the Binding. This works as expected.
The problem is that, if SourceUpdateTrigger is PropertyChanged, when the user starts typing, formatting is applied after entering the first character and the caret is then positioned before the character just entered. This means that the next character will be entered before the first instead of after. If SourceUpdateTrigger is LostFocus then the user must shift focus to another control before the OK button is enabled, which occurs after validating the currency field.
What I was hoping to do was handle the GotFocus and LostFocus events, get a reference to the Binding and change its StringFormat property. I have no issue with this in regards to MVVM because it's a purely UI issue. The problem is that an exception was thrown and I was told that the Binding could not be changed after being used.
I've considered various other options, including a custom converter. That didn't work though, because I couldn't work out how to use the ConverterParameter to expose the control's IsFocused property to the converter.
Anyone have any ideas?

You can use a data trigger in a data template to dynamically change your binding for a text box.
<DataTemplate x:Key="DataTemplate1">
<TextBox x:Name="TheTextBox"
Text="{Binding Path=ThePropertyPath, StringFormat={}{0:p0}, UpdateSourceTrigger=PropertyChanged}" />
<DataTemplate.Triggers>
<!-- Set the TextBox.Text Binding but this time leave the StringFormat off -->
<DataTrigger Binding="{Binding Path=IsKeyboardFocusWithin, ElementName=TheTextBox}" Value="True">
<Setter TargetName="TheTextBox" Property="Text" Value="{Binding Path=ThePropertyPath, UpdateSourceTrigger=PropertyChanged}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsFocused, ElementName=TheTextBox}" Value="True">
<Setter TargetName="TheTextBox" Property="Text" Value="{Binding Path=ThePropertyPath, UpdateSourceTrigger=PropertyChanged}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>

Related

Toggle two different elements in DataTemplate using Style Triggers

I have an object in ViewModel whose properties are displayed by a datatemplate. The screen also has a button toggling the IsEditing flag in ViewModel, which should make the object properties editable, like the following:
Name should change from TextBlock to TextBox;
Color should change from colored rectangle to ComboBox with color options;
Category should change from TextBlock to ComboBox;
I know how to implement this with two completely independent DataTemplates, using a Style and a DataTrigger to toggle between them:
<ContentControl Content="{Binding FancyObject}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate" Value="{StaticResource DisplayTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsEditing, ElementName=UserControl}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource EditTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
And currently the DisplayTemplate is like this:
<DataTemplate x:Key="DisplayTemplate" DataType="my:FancyObject">
<Border>
<DockPanel DataContext="{Binding Metadata}">
<Border>
<TextBlock Text="{Binding Name}"/>
</Border>
<DataGrid
AutoGenerateColumns="False"
ItemsSource="{Binding FancyObjectCollection}">
<DataGrid.Columns>
<!-- Text and Template columns -->
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Border>
</DataTemplate>
The problem is: using two independent but similar templates would mean a duplication of layout, since only some fields would change, but the overall structure is the same.
Another option I imagine is to use a single template defined inside the Style, and use the Trigger to change the fields individually, but I don't know how to do it, or even if it is possible at all.
You can use one template.
In the template add both TextBlock and TextBox, same for all your other controls on the original template.
Bind the controls visibility to bool to visibility converter. (Or use triggers) Only one set of your control will be seen each time (based on IsEditing flag)
The ControlTemplate is only used upon generating the UI Elements. If you change the template AFTER generating the items, the generated items will not change.
You can also not use a trigger to change a TextBox to a TextBlock and vice versa.
Your only option is indeed to mirror the layout twice and hide/display it via the data bound property.

Binding IsEnabled to Text.Length only works one way

I have a TextBox, which I want to be enabled only if another TextBox has text in it. I am binding the Text.Length property of the first TextBox to the IsEnabled property on the second box. I have also tried binding the Text property of the first box and using a converter to convert to a bool. Both methods result in the second box being enabled when text is entered into the first but when the text is deleted the second box isn't disabled.
I have tried setting NotifyOnSourceUpdated and NotifyOnTargetUpdated to true but neither has any effect.
<TextBox Name="textBox1"/>
<TextBox Name="textBox2" IsEnabled="{Binding ElementName=textBox1, Path=Text.Length}"/>
So my question is what is needed for textBox2 to be disabled when the text in textBox1 is deleted.
This should work -
<TextBox Name="textBox1"/>
<TextBox Name="textBox2">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Text.Length, ElementName=textBox1}"
Value="0">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Issue with your code is you are binding IsEnabled bool property to Text.Length property whose type is int. So, either you need to use the converter or do it through triggers as i posted above.

Conditionally changing the Foreground of a WPF ComboBox item depending upon a value in the databound ItemsSource item

I have a WPF ComboBox bound to a Collection List<Users>. I have applied a DataTemplate to show the FirstName using a TextBlock and this works as expected:
<ComboBox Margin="5" ItemsSource="{Binding Path=TheUsers}" Name="cboUsers">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Margin="10" Text="{Binding Path=FirstName}">
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>`
I have an item in my User class called IsActive which is a Boolean value. If true then I want to set the Foreground of the TextBlock to Navy.
I have spent so much time on what should be so easy and looked all over the web but most articles talk about changing the overall colour or binding to another element in the xaml.
I tried implementing a DataTrigger and after an hour removed the code because it was not working. It would not recognise my field name. Does anyone have a very simple guide to how to do this or what would be the best approach?
As you apparently are not dealing with fields after all, this style should do what you want:
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsActive}" Value="True">
<Setter Property="Foreground" Value="Navy"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
It would not recognise my field name.
You cannot bind to fields, end of story.

FrameworkElement`s DataContext Property does NOT inherit down the element tree

Hello WPF Pros at least I hope some of you read this!
DataContext is a property on FrameworkElement (base class for all WPF Controls) and is implemented as a DependencyProperty. That means all the descendant elements in the logical tree share the same DataContext.
So the ContentControl should do it with its descendant elements right?
I have a scenario where that is NOT the case and I would like to know WHAT is the cause of that misbehaviour ?!
That you understand a bit more about it please read this thread ( dont NOT want to copy everything here) where the trouble starts...:
WPF: Can not find the Trigger target 'cc'. The target must appear before any Setters, Triggers
and to say it in short words: My DataTemplates within the ContentControl do have a dead DataContext that means there is NOTHING to bind to it, what is actually not possible...
Every Element down the ContentControl has NOTHING set in the DataContext Property ???
DataContext is a property on
FrameworkElement (base class for all
WPF Controls) and is implemented as a
DependencyProperty. That means all the
descendant elements in the logical
tree share the same DataContext.
The fact that it's a dependency property doesn't imply inheritance... It's true for DataContext, but only because the dependency property has the FrameworkPropertyMetadataOptions.Inherits flag in its metadata.
So the ContentControl should do it
with its descendant elements right?
ContentControl is a bit special: the DataContext of its descendants (the visual tree built from the DataTemplate) is actually be the Content of the ContentControl. So if your ContentControl has no content, the DataContext inside it is null.
This worked for me:
<ContentControl ContentTemplate="{StaticResource NotesTemplate}"
Content="{Binding}"
DataContext="{Binding HeightField}"/>
Without the Content="{Binding}", the DataContext was NULL
The last answer (from VinceF) worked for me too.
I wanted to show a usercontrol depending on the value of a property in my viewmodel. So I made a ContentControl with some Style Triggers. Depending on the value of a bind property the trigger sets a specific ContentTemplate containing the specific usercontrol.
The usercontrol was shown right, but its DataContext was always null. So I had to set the Context of the ContentControl to: Content="{Binding}" After that, the UserControls worked fine and had the same DataContext as their parent.
So my XAML looks like that:
In the Resources part I defined two DataTemplates; each one for each UserControl I want to show.
<DataTemplate x:Key="ViewA">
<namespace:UserControlA/>
</DataTemplate>
<DataTemplate x:Key="ViewB">
<namespace:UserControlB/>
</DataTemplate>
The part where I show the UserControl depending on a property is the following:
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Property}" Value="0">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource ViewA}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Property}" Value="1">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource ViewB}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
after reading this question and previous answers, I prefer using ContentControl with data triggered Content like this:
Controls which will be set as Content of ContentControl:
<TextBox x:Key="ViewA">
...
</TextBox>
<ComboBox x:Key="ViewB">
...
</ComboBox>
ContentControl which switch own content by DataTrigger in ContentControl style:
<ContentControl>
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Property}" Value="0">
<Setter Property="Content" Value="{StaticResource ViewA}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Property}" Value="1">
<Setter Property="Content" Value="{StaticResource ViewB}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
I hope this helps to someone like previous answers to me.

WPF Button Visibility

I am using MVVM architecture to develop a WPF application...
So far everything has been going fine.
I have run into an issue with binding visibility. I want to minimize writing code in the code behind if i can but if it's REQUIRED then I don't mind doing it.
I have a ViewModel. THis model exposes a boolean and 2 commands. A connect command, a disconnect command, and a DeviceCurrentlyConnected Bool.
Basically I have decided to make 2 buttons but have the button visibility based on the boolean.
So i have had a hard time with this. I tried styles with triggers for a long time.
<Button Visibility="Hidden" Content="{x:Static UIStrings:ClientStrings.DeviceBar_DisconnectCommandName}" VerticalAlignment="Center" HorizontalAlignment="Center" Height="{Binding ElementName=this.Content, Path=DesiredHeight}" Margin="10" Name="Disconnect" Command="{Binding DisconnectCurrentDeviceCommand}">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding DataCotext.DeviceConnected, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
I can not get styles to work at all.
Basically the functionality that I want is:
DeviceConnected = false:
Display a button with content Connect and command bound to the ConnectCommand.
DeviceConnected = true:
Display a button with content disconnect and command bound to the DisconnectCommand.
for a button to be displayed and bound to the connect device when no device is currently connect and for a button to be displayed when a device is connected that is bound to the disconnect command and to say the word disconnect.
Write up a bool to visibility converter and then use the converter on your buttons. Five minute recipe for a decent BoolToVisibilityConverter is a good post to read up on creating/using a visibility converter.
What I've done in the past is use a bool to visibility converter and passed in the button's IsEnabled property as the parameter to the converter. Since the button is dis/enabled by the command in the model with the CanExecute method, you can then use the IsEnabled property to set the visibility of the button with the converter.
The reason that your trigger doesn't work is that the style is overridden by the attribute on the button itself.
You can use a converter as Metro Smurf suggests, alternatively you can move the visibility attribute into the style so that the trigger works properly
Just add:
<Style.Setters>
<Setter Property="Visibility" Value="Hidden" />
</Style.Setters>
To the style and then remove the attribute.

Resources