WPF TextBox StringFormat not working with PropertyChanged - wpf

I have a problem. I need to have double formatted values in all TextBoxes.
When you type something into this, after lost focus it will be formatted.
<TextBox Text="{Binding ABC, StringFormat='{}{0:N}'}" />
Problem arises when you add this UpdateSourceTrigger with propertychanged. Then it will never be formatted.
<TextBox Text="{Binding ABC, UpdateSourceTrigger=PropertyChanged, StringFormat='{}{0:N}'}" />
Why is that? Is there any way how to solve that? (in XAML preferably)

Try this
<TextBox x:Name="test" Text="{Binding MyName, UpdateSourceTrigger=Explicit,StringFormat='{}{0:N}'}" TextChanged="test_TextChanged" Width="100" Height="30" />
private void test_TextChanged(object sender, TextChangedEventArgs e)
{
BindingExpression exp = test.GetBindingExpression(TextBox.TextProperty);
exp.UpdateSource();
}

Related

Wpf Combobox with databinding: initial value is empty

In the following xaml fragment SessoList is a list of string ("M" and "F").
<ComboBox IsEditable="False" Margin="5" SelectedValue="{Binding Sesso}" ItemsSource="{Binding SessoList}" Width="40" Height="28"/>
The combobox works as expected and it is pre-populated reflecting the value of Sesso in the viewmodel.
The combobox selectable items are only two and fixed so I tried to simplify defining them in xaml:
<ComboBox IsEditable="False" Margin="5" SelectedValue="{Binding Sesso}" SelectedValuePath="{Binding Tag}" Width="40" Height="28" Name="Primo">
<ComboBoxItem Content="M" Tag="M" />
<ComboBoxItem Content="F" Tag="F" />
</ComboBox>
This combobox is capable of updating the viewmodel property sesso, but is not pre-populated with the correct value.
The following error is reported:
BindingExpression path error: 'Tag' property not found on 'object'
How can I successfully define the combobox items in xaml and have it display the right value based on SelectedValue databinding ?
Forgot to mention I'm using .Net 4.0
As I can understand, you want to define the ComboBox ItemsSource in XAML,
here is the solution that worked for me:
Xaml Window resources:
<Window.Resources>
<x:Array x:Key="Array" Type="{x:Type nirHelpingOvalButton:ComboObjectModel}">
<nirHelpingOvalButton:ComboObjectModel Content="M_Content" Tag="M_Tag"/>
<nirHelpingOvalButton:ComboObjectModel Content="F_Content" Tag="F_Tag"/>
</x:Array>
Xaml Combo:
<Grid>
<ComboBox IsSynchronizedWithCurrentItem="True" IsEditable="False" SelectedIndex="0" Margin="5" ItemsSource="{StaticResource Array}"
SelectedValue="{Binding Content, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedValuePath="Tag" Width="90" Height="28" Name="Primo">
<i:Interaction.Behaviors>
<nirHelpingOvalButton:CustomComboSelectionBehavior/>
</i:Interaction.Behaviors>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Content}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox></Grid>
View model combo SelectedValue binded property:
public string Content
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged("Content");
}
}
List item Behavior Code:
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
var firstItem = AssociatedObject.ItemsSource.Cast<object>().FirstOrDefault();
AssociatedObject.SelectedItem = firstItem;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= OnLoaded;
}
regards

TextBox BindingExpression UpdateSourceTrigger=Explicit

Need a TextBox in a ListView DataTemplate to call set on either LostFocus or enter key. Used UpdateSourceTrigger = Explicit and events for LostFocus and KeyUp. Problem is that I cannot get a valid reference to the BindingExpression.
XAML
<ListView x:Name="lvMVitems" ItemsSource="{Binding Path=DF.DocFieldStringMVitemValues, Mode=OneWay}">
<ListView.View>
<GridView>
<GridViewColumn x:Name="gvcExistingValue">
<GridViewColumnHeader Content="Value"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="tbExistingValue"
Text="{Binding Path=FieldItemValue, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, NotifyOnValidationError=True, UpdateSourceTrigger=Explicit}"
Validation.Error="Validataion_Error"
LostFocus="tbExistingValue_LostFocus" KeyUp="tbExistingValue_KeyUp" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Code Behind NOT working
private void tbExistingValue_LostFocus(object sender, RoutedEventArgs e)
{
BindingExpression be = lvMVitems.GetBindingExpression(ListView.SelectedItemProperty);
be.UpdateSource();
}
be is null. I have tried ListView.SelectedValueProperty and ListView.SelectedPathProperty. If it try tbExistingValue it fails with a message "does not exists" and will not even compile. How do I get the proper BindingExpression?? Thanks.
If I set UpdateSourceTrigger = LostFocus and remove the event handlers it does call set properly. There is a valid twoway binding there. I just cannot get a valid reference to BindingExpression (be) using explicit.
It works fine for a TextBox directly on page (in a grid cell). The xaml below works:
<TextBox Grid.Row="1" Grid.Column="1" x:Name="strAddRow"
Text="{Binding Path=DF.NewFieldValue, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True, UpdateSourceTrigger=Explicit}"
Validation.Error="Validataion_Error"
LostFocus="strAddRow_LostFocus" KeyUp="strAddRow_KeyUp"/>
This BindingExpression works fine:
private void strAddRow_LostFocus(object sender, RoutedEventArgs e)
{
BindingExpression be = strAddRow.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
}
Since you apply the binding on your Textbox's Text DP, so you need to fetch the binding from there only like this -
private void tbExistingValue_LostFocus(object sender, RoutedEventArgs e)
{
BindingExpression be = (sender as TextBox).GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
}
Moreover, you haven't bind the ListView SelectedItem with any property of your ViewModel. To retrieve the binding, it should be atleast binded to some value. So, you should bind it to your FieldValueProperty then you won't get null value with your code in place.
You do not need to use UpdateSourceTrigger on TextBox using the event LostFocus.
It's the functioning by default.
Answer here : https://msdn.microsoft.com/en-gb/library/system.windows.data.binding.updatesourcetrigger(v=vs.110).aspx

ListBox with hyperlink -> selection changed

I want to do with xaml bindings such feature:
Listbox contains hyperlinks.
When hyperlink clicked - we go to another frame
But also SelectedItem must changed, and on another frame we show info about selected item.
I want it without subscribing click/selected events. Only declarative
Example of my listbox
<ListBox Grid.Row="1"
x:Name="OrderTypesListBox"
ItemsSource="{Binding OrderTypes, Mode=OneWay}"
SelectedItem="{Binding SelectedCall.OrderType, Mode=TwoWay}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<HyperlinkButton Style="{StaticResource LinkStyle}" NavigateUri="/WindowPage" TargetName="ContentFrame" Content="WindowPage"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now solve like that
<ListBox Grid.Row="1"
x:Name="OrderTypesListBox"
ItemsSource="{Binding OrderTypes, Mode=OneWay}"
SelectedItem="{Binding SelectedCall.OrderType, Mode=TwoWay}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<HyperlinkButton
TargetName="ContentFrame"
NavigateUri="{Binding OrderTypeNextPage}"
Content="{Binding Name}"
Click="HyperlinkButton_Click"
Tag="{Binding}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
OrderTypesListBox.SelectedItem = (sender as HyperlinkButton).Tag;
}
Don't use a HyperlinkButton. Perform the needed actions when the SelectedItem changes in your ViewModel.
Edit: If you need to respond to all click events even if the item is already selected then you can use a Behavior to do this. Just create a behavior for the TextBlock that navigates on the TextBlock click event.
Edit2: Behaviors are pretty simple to code up and easy to use (and don't break the MVVM paradigm).
public class NavigatingTextBlockBehavior : Behavior<TextBlock>
{
protected override void OnAttached()
{
AssociatedObject.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseDown);
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
NavigationService.Navigate(new Uri("/WindowPage"));
}
}

Looking for a WPF ComboBox with checkboxes

My google skills fail me. Anyone heard of a control like that for WPF. I am trying to make it look like this (winforms screenshot).
You can do this yourself by setting the DataTemplate of the combo box. This article shows you how - for a listbox, but the principle is the same.
Another article here is perhaps a better fit for what you are trying to do, simple set the first column of the item template to be a checkbox and bind it to a bool on your business object.
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}"
Width="20" />
<TextBlock Text="{Binding DayOfWeek}"
Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
There is my combobox. I use Martin Harris code and code from this link Can a WPF ComboBox display alternative text when its selection is null?
<ComboBox Name="cbObjects" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" Margin="2,2,6,0" SelectionChanged="OnCbObjectsSelectionChanged" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" Width="20" VerticalAlignment="Center" Checked="OnCbObjectCheckBoxChecked" Unchecked="OnCbObjectCheckBoxChecked" />
<TextBlock Text="{Binding ObjectData}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock IsHitTestVisible="False" Name="tbObjects" Text="Выберите объекты..." Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" Margin="6,2,6,0" />
Small class for datasource:
public class SelectableObject <T> {
public bool IsSelected { get; set; }
public T ObjectData { get; set; }
public SelectableObject(T objectData) {
ObjectData = objectData;
}
public SelectableObject(T objectData, bool isSelected) {
IsSelected = isSelected;
ObjectData = objectData;
}
}
And there is two handler - one for handling CheckBox clicked and one for forming Text for ComboBox.
private void OnCbObjectCheckBoxChecked(object sender, RoutedEventArgs e) {
StringBuilder sb = new StringBuilder();
foreach (SelectableObject<tblObject> cbObject in cbObjects.Items)
{
if (cbObject.IsSelected)
sb.AppendFormat("{0}, ", cbObject.ObjectData.Description);
}
tbObjects.Text = sb.ToString().Trim().TrimEnd(',');
}
private void OnCbObjectsSelectionChanged(object sender, SelectionChangedEventArgs e) {
ComboBox comboBox = (ComboBox)sender;
comboBox.SelectedItem = null;
}
For ComboBox.ItemsSource I use
ObservableCollection<SelectableObject<tblObject>>
where tblObject is type of my object, a list of which I want to display in ComboBox.
I hope this code is useful to someone!
Give a try to CheckComboBox from Extended WPF Toolkit.
The main advantage for me is having two lists for binding:
all items available for selection
just selected items
I find this approach more practical. In addition you can specify value and display members of the collections you're binding.
If you don't want to bring a bunch of other controls with CheckComboBox, you can get the source code of it, it's pretty straightforward (need to bring Selector class as well).
ComboBox with Checkboxes
<ComboBox Height="16" Width="15">
<CheckBox Content="First Checkbox" />
<CheckBox Content="Second Checkbox" />
<CheckBox Content="Third Checkbox" />
<TextBlock Text="Some Text" />
</ComboBox>
The provided answers surprisingly didn't work for me, I tried many variations and kept getting error messages about the checkbox not being part of combobox and the data context seemed to be broken.
In the end I didn't have to do anything involving data templates or any code behind and my bindings are working fine (not shown in example)
I must say I'm happy with how easy this turned out to be after reading all the answers.

Focus DataTemplate in WPF

The behaviour I am looking for is that a selection of a Item in a ListView results in focusing the first focusable visualchild.
Problem: datatemplated data in a ItemsControler which does not get an initial focus.
In the example below there are 4 Strings which are then stuffed into a TextBox via Datatemplate.
Example:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ListView>
<ListView.Resources>
<DataTemplate DataType="{x:Type sys:String}" >
<TextBox Width="100" Text="{Binding Mode=OneWay}"/>
</DataTemplate>
</ListView.Resources>
<ListView.ItemsSource>
<x:Array Type="{x:Type sys:String}">
<sys:String>test</sys:String>
<sys:String>test</sys:String>
<sys:String>test</sys:String>
<sys:String>test</sys:String>
</x:Array>
</ListView.ItemsSource>
</ListView>
</Grid>
</Window>
I already tried some combinations of
FocusManager.FocusedElement="{Binding ElementName=[...]}"
pointless to say: without success.
Anyone a clou how I could get what I want without traversing the visual tree in c#?
It should be possible to do this, shouldn't it?
The FocusManager works sweet for this.
<DataTemplate x:Key="MyDataTemplate" DataType="ListBoxItem">
<Grid>
<WrapPanel Orientation="Horizontal" FocusManager.FocusedElement="{Binding ElementName=tbText}">
<CheckBox IsChecked="{Binding Path=Completed}" Margin="5" />
<Button Style="{StaticResource ResourceKey=DeleteButtonTemplate}" Margin="5" Click="btnDeleteItem_Click" />
<TextBox Name="tbText"
Text="{Binding Path=Text}"
Width="200"
TextWrapping="Wrap"
AcceptsReturn="True"
Margin="5"
Focusable="True"/>
<DatePicker Text="{Binding Path=Date}" Margin="5"/>
</WrapPanel>
</Grid>
</DataTemplate>
Using the binding syntax in C# will not work, because that is how bindings are described in XAML. It might work to create a new instance of System.Windows.Data.Binding, and set its properties to the equivalent of what you would set in XAML, but I'm not sure what ElementName that you would be able to bind to in order to get this working correctly.
The only other option that I can think of is to add a handler for the SelectionChanged event, and set the focus manually, but that doesn't sound like the solution that you want.
Upon further inspection, I don't think that a Binding solution is possible. FocusManager.FocusedElement is an IInputElement, which doesn't have any members related to binding. I think that you need to traverse the visual tree in order to find the first object that is focusable. Something like this should work:
// Event handler for the ListBox.SelectionChanged event
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox listBox = sender as ListBox;
ItemContainerGenerator = generator.listBox.ItemContainerGenerator;
ListBoxItem selectedItem =
(ListBoxItem)generator.ContainerFromIndex(listBox.SelectedIndex);
IInputElement firstFocusable = FindFirstFocusableElement(selectedItem);
firstFocusable.Focus();
}
private IInputElement FindFirstFocusableElement(DependencyObject obj)
{
IInputElement firstFocusable = null;
int count = VisualTreeHelper.GetChildrenCount(obj);
for(int i = 0; i < count && null == firstFocusable; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
IInputElement inputElement = child as IInputElement;
if(null != inputElement && inputElement.Focusable)
{
firstFocusable = inputElement;
}
else
{
firstFocusable = FindFirstFocusableElement(child);
}
}
return firstFocusable;
}

Resources