How to bind multiple textboxes with a single textblock in wpf - c#

I have 10 textboxes and I want there addition to be shown in a single textblock on lost_focus UpdateSourceTrigger property.

If you need to update the sum on TextBox lost focus event you can use classic events. This is the XAML (I used just four TextBoxes, but it is easy to extend):
<StackPanel>
<TextBox Name="txt01" Margin="3" HorizontalAlignment="Stretch" LostFocus="txt_LostFocus" TextChanged="txt_TextChanged" />
<TextBox Name="txt02" Margin="3" HorizontalAlignment="Stretch" LostFocus="txt_LostFocus" TextChanged="txt_TextChanged" />
<TextBox Name="txt03" Margin="3" HorizontalAlignment="Stretch" LostFocus="txt_LostFocus" TextChanged="txt_TextChanged" />
<TextBox Name="txt04" Margin="3" HorizontalAlignment="Stretch" LostFocus="txt_LostFocus" TextChanged="txt_TextChanged" />
<TextBlock Name="sum" Margin="3,10,3,3" />
</StackPanel>
In the code you have the event handlers:
private void txt_LostFocus(object sender, RoutedEventArgs e)
{
int value1;
int value2;
TextBox textBox = (TextBox)sender;
if (textBox.Tag is bool && (bool)textBox.Tag)
{
if (Int32.TryParse(textBox.Text, out value1))
{
if (String.IsNullOrEmpty(sum.Text))
{
sum.Text = textBox.Text;
}
else
{
Int32.TryParse(sum.Text, out value2);
sum.Text = Convert.ToString(value1 + value2);
}
}
textBox.Tag = false;
}
}
private void txt_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
textBox.Tag = true;
}
On the other side, if you can give up the "LostFocus" requirement, you can use MultiBinding (in this case it works just in "PropertyChanged mode", since TextBoxes are now the sources):
<StackPanel>
<TextBox Name="txt01" Margin="3" HorizontalAlignment="Stretch" />
<TextBox Name="txt02" Margin="3" HorizontalAlignment="Stretch" />
<TextBox Name="txt03" Margin="3" HorizontalAlignment="Stretch" />
<TextBox Name="txt04" Margin="3" HorizontalAlignment="Stretch" />
<TextBlock Name="sum" Margin="3,10,3,3">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource AddValueConverter}" Mode="OneWay">
<MultiBinding.Bindings>
<Binding ElementName="txt01" Path="Text" />
<Binding ElementName="txt02" Path="Text" />
<Binding ElementName="txt03" Path="Text" />
<Binding ElementName="txt04" Path="Text" />
</MultiBinding.Bindings>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
You just need to write a simple converter:
public class AddValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
int sum = 0;
int result;
foreach(object value in values)
{
if (Int32.TryParse(System.Convert.ToString(value), out result))
{
sum += result;
}
}
return System.Convert.ToString(sum);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

Related

Changing background color with a converter in XAML

TextBlock background color not changing.
I've bound my data to a TextBlock which updates with INotifyPropertyChanged, and the converter does fire.
public class Oddsindicator : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string myPrice = "0";
string tradePrice = "0";
var colorRed = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FFB0E0E6");
var colorWhite = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("White");
var unchanged = new SolidColorBrush(colorRed);
var changed = new SolidColorBrush(colorGreen);
if (values[0] != DependencyProperty.UnsetValue)
{
myPrice = values[0].ToString();
tradePrice = values[1].ToString();
}
if (myPrice == tradePrice)
{
return unchanged;
}
else
{
return changed;
}
}
}
XAML:
<Window.Resources>
<local:Oddsindicator x:Key="Oddsindicator" >
</local:Oddsindicator>
</Window.Resources>
<TextBlock Text="{Binding BackPrice, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" TextAlignment="Center" Margin="1" Grid.Row="4" Grid.Column="2" />
<TextBlock>
<TextBlock.Background>
<MultiBinding Converter="{StaticResource Oddsindicator}">
<Binding Path="BackPrice"/>
<Binding Path="Lasttradedprice" />
</MultiBinding>
</TextBlock.background>
</TextBlock>
I've used break points at the return and they both fire. My bound value updates perfectly. The converters comparing the values and giving the correct results, just not updating the TextBlock.
(This should in fact be a comment but I need the formatting features of an answer)
You have two TextBlocks. The second (for which you set the background) has no Text and is probably of 0 size.
Try putting the TextBlock.Background in the first TextBlock:
<TextBlock Text="{Binding BackPrice, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" TextAlignment="Center" Margin="1" Grid.Row="4" Grid.Column="2" >
<TextBlock.Background>
<MultiBinding Converter="{StaticResource Oddsindicator}">
<Binding Path="BackPrice"/>
<Binding Path="Lasttradedprice" />
</MultiBinding>
</TextBlock.Background>
</TextBlock>

Disable button when validating WPF

I made a rule validation on a property. What I want is that when the condition is not satisfied the save button should be disabled. How can I do it? This is how it looks:
image. The button is not disabled.
This is my code
public string FieldName { get; set; }
public Regex regularExpression = new Regex("^[a-zA-Z]*$");
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
//var stringValue = value as string;
if (FieldName == "Age")
return AgeValidate(value);
if (FieldName == "Name")
return NameValidation(value);
return new ValidationResult(true, null);
}
private ValidationResult NameValidation(object value)
{
var onlyCharacters = regularExpression.IsMatch((string)value);
if (onlyCharacters)
return new ValidationResult(true, null);
else
return new ValidationResult(false, $"Only alfabetical charaters");
}
In XAML:
<Label Grid.Row="0" Grid.Column="0" Margin="103,0,98,10" Content="Name : " VerticalContentAlignment="Center" HorizontalContentAlignment="Right" Grid.ColumnSpan="2"/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtCourtName" Margin="113,4,-55,6" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" Validation.ErrorTemplate="{StaticResource errorTemplate}" >
<TextBox.Text>
<Binding Path="CourtName" ValidatesOnDataErrors="true" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local11:AuthValidation FieldName="Name"></local11:AuthValidation>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Button:
<Button Content="Save" x:Name="btnSaveCourt" Command="{Binding SaveCourt}" Margin="2,10" Width="119" Background="#909090" Foreground="Black" BorderBrush="White" />
And my template for error:
<ControlTemplate x:Key="errorTemplate">
<Border BorderBrush="OrangeRed" BorderThickness="2">
<Grid>
<AdornedElementPlaceholder>
<TextBlock Text="{Binding [0].ErrorContent}" Foreground="OrangeRed"
VerticalAlignment="Center" HorizontalAlignment="Right"
Margin="-220"
>
</TextBlock>
</AdornedElementPlaceholder>
</Grid>
</Border>
</ControlTemplate>
You could bind the button's IsEnabled to a property, a bool propfull (write propfull tab tab and it will write it out for you in visual studio).
Or you could use a MultiDataTrigger on the button and bind all the HasError property of the textboxes you want to validate.
Or if you use a command you could use the can execute property of it.

How do I bind combobox IsChecked to 4 buttons from the same group?

I want the combo box to be enable when pressing one of the radio buttons.
<RadioButton x:Name="A" GroupName="rButton" Content="A" Grid.Column="4"/>
<RadioButton x:Name="B" GroupName="rButton" Content="B" Grid.Column="4"/>
<RadioButton x:Name="C" GroupName="rButton" Content="C" Grid.Column="4"/>
<RadioButton x:Name="D" GroupName="rButton" Content="D" Grid.Column="4"/>
<ComboBox IsEnabled="{Binding IsChecked,?? }" Grid.Column="5" Width="120" Height="30"/>
If you want to solve this via Bindings (and you should), you need a MultiBindingConverter that returns true as long as one of the values is true (boolean OR):
public class BooleanOrConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
foreach (object value in values)
{
if (value is bool && (bool) value)
return true;
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return Enumerable.Repeat(DependencyProperty.UnsetValue, targetTypes.Length).ToArray();
}
}
Definition:
<Window.Resources>
<local:BooleanOrConverter x:Key="OrConverter"/>
</Window.Resources>
Usage:
<RadioButton x:Name="RadioButtonSource" GroupName="rButton" Content="A" Grid.Column="4"/>
<RadioButton x:Name="RadioButtonToken" GroupName="rButton" Content="B" Grid.Column="4"/>
<RadioButton x:Name="RadioButtonII" GroupName="rButton" Content="C" Grid.Column="4"/>
<RadioButton x:Name="RadioButtonUkey" GroupName="rButton" Content="D" Grid.Column="4"/>
<ComboBox Grid.Column="5" Width="120" Height="30">
<ComboBox.IsEnabled>
<MultiBinding Converter="{StaticResource OrConverter}">
<Binding ElementName="RadioButtonSource" Path="IsChecked"/>
<Binding ElementName="RadioButtonToken" Path="IsChecked"/>
<Binding ElementName="RadioButtonII" Path="IsChecked"/>
<Binding ElementName="RadioButtonUkey" Path="IsChecked"/>
</MultiBinding>
</ComboBox.IsEnabled>
</ComboBox>
This way, as soon as any of the RadioButtons's IsChecked properties becomes true, the ComboBox is enabled. If you reset the RadioButtons, it get's disabled again.

Bind Settings to a group of RadioButtons

I have a kind of annoying scenario here.
In my WPF GUI I declared some RadioButtons, I want the right one to be checked when the GUI loads.
XAML:
<RadioButton Grid.Row="0" Grid.Column="0" Name="RadioButtonShowSettings" GroupName="OnTrayClick" Content="Show settings window" HorizontalAlignment="Left" VerticalAlignment="Center" />
<RadioButton Grid.Row="1" Grid.Column="0" Name="RadioButtonOpenFile" GroupName="OnTrayClick" Content="Open upload dialog" HorizontalAlignment="Left" VerticalAlignment="Center" />
<RadioButton Grid.Row="2" Grid.Column="0" Name="RadioButtonIndexFile" GroupName="OnTrayClick" Content="Open file indexer" HorizontalAlignment="Left" VerticalAlignment="Center" />
<RadioButton Grid.Row="0" Grid.Column="1" Name="RadioButtonImageGallery" GroupName="OnTrayClick" Content="Open image gallery" HorizontalAlignment="Left" VerticalAlignment="Center" />
<RadioButton Grid.Row="1" Grid.Column="1" Name="RadioButtonTakeScreenshot" GroupName="OnTrayClick" Content="Take a screenshot (3 seconds delay)" HorizontalAlignment="Left" VerticalAlignment="Center" />
To keep possible bugs to a minimum I created a Property in my HonkySettings of the type String called TrayIconBehaviour. It contains the ContentProperty of the currently checked RadioButton.
I've been doing the loading programatically with a LoadSettings() function, but I'd like to remove that and do something more appealing with Bindings.
LoadSettings:
private void LoadSettings()
{
List<RadioButton> TrayIconBehaviourRadioButtons = GridTrayIconBehaviour.Children.OfType<RadioButton>().ToList();
foreach (RadioButton rButton in TrayIconBehaviourRadioButtons)
{
if (rButton.Content.Equals(HonkySettings.Default.TrayIconBehaviour))
rButton.IsChecked = true;
}
List<RadioButton> FullscreenCaptureRadioButtons = GridFullscreenCapture.Children.OfType<RadioButton>().ToList();
foreach (RadioButton rButton in FullscreenCaptureRadioButtons)
{
if (rButton.Content.Equals(HonkySettings.Default.FullscreenCapture))
rButton.IsChecked = true;
}
if (RadioButtonQualityPNG.Content.Equals(HonkySettings.Default.ScreenCaptureQuality))
RadioButtonQualityPNG.IsChecked = true;
else RadioButtonQualityJPG.IsChecked = true;
}
I've got HonkyGuiControls, which contains WPF User Controls that I use in my HonkySettingsWindow, if needed, I'd be ready to create a custom RadioButton to bind my settings to them.
I already tried to create a User Control called CustomRadioButton and bind its IsCheckedProperty to something like this:
public Boolean IsChecked
{
get
{
if (CustomRadioButton.Content.Equals(HonkySettings.Default.TrayIconBehaviour)) return true;
else return false;
}
set
{
HonkySettings.Default.TrayIconBehaviour = CustomRadioButton.Content.ToString();
}
}
But the CustomRadioButton IsChecked Property wouldn't bind to it, because I'd need to create a DependencyProperty, and I have no idea how I should do the same thing with a DependencyProperty. Help please.
If you want to this using binding, try this
xaml
<Window x:Class="Stackoverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Stackoverflow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:SettingsConverter x:Key="settingsConverter"/>
</Window.Resources>
<StackPanel>
<RadioButton GroupName="OnTrayClick" Content="Show settings window">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource myNameConverter}">
<Binding Path="HonkySettings.Default.TrayIconBehaviour"/>
<Binding Path="Content" RelativeSource="{RelativeSource Mode=Self}"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
<RadioButton GroupName="OnTrayClick" Content="Open upload dialog">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource myNameConverter}">
<Binding Path="HonkySettings.Default.TrayIconBehaviour"/>
<Binding Path="Content" RelativeSource="{RelativeSource Mode=Self}"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
<RadioButton GroupName="OnTrayClick" Content="Open file indexer">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource myNameConverter}">
<Binding Path="HonkySettings.Default.TrayIconBehaviour"/>
<Binding Path="Content" RelativeSource="{RelativeSource Mode=Self}"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
<RadioButton GroupName="OnTrayClick" Content="Open image gallery">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource myNameConverter}">
<Binding Path="HonkySettings.Default.TrayIconBehaviour"/>
<Binding Path="Content" RelativeSource="{RelativeSource Mode=Self}"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
<RadioButton GroupName="OnTrayClick" Content="Take a screenshot (3 seconds delay)">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource myNameConverter}">
<Binding Path="HonkySettings.Default.TrayIconBehaviour"/>
<Binding Path="Content" RelativeSource="{RelativeSource Mode=Self}"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
</StackPanel>
Converter
public class SettingsConverter:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values != null && values.Count() == 2)
return values.First() == values.Last();
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
if you making alot of this kind of binding as i can see there so to reduce xaml code we can do it this way as below
xaml
<Window x:Class="Stackoverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Stackoverflow"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<RadioButton GroupName="OnTrayClick" Content="Show settings window" IsChecked="{local:RadioButtonBinding HonkySettings.Default.TrayIconBehaviour}"/>
<RadioButton GroupName="OnTrayClick" Content="Open upload dialog" IsChecked="{local:RadioButtonBinding HonkySettings.Default.TrayIconBehaviour}"/>
<RadioButton GroupName="OnTrayClick" Content="Open file indexer" IsChecked="{local:RadioButtonBinding HonkySettings.Default.TrayIconBehaviour}"/>
<RadioButton GroupName="OnTrayClick" Content="Open image gallery" IsChecked="{local:RadioButtonBinding HonkySettings.Default.TrayIconBehaviour}"/>
<RadioButton GroupName="OnTrayClick" Content="Take a screenshot (3 seconds delay)" IsChecked="{local:RadioButtonBinding HonkySettings.Default.TrayIconBehaviour}"/>
</StackPanel>
RadioButtonBinding
public class RadioButtonBinding : MultiBinding
{
public RadioButtonBinding(string propName)
{
Bindings.Add(new Binding(propName));
Bindings.Add(new Binding("Content") { RelativeSource = new RelativeSource(RelativeSourceMode.Self) });
Converter = new SettingsConverter();
}
}
converter
public class SettingsConverter:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values != null && values.Count() == 2)
return values.First() == values.Last();
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Pass current item to ValueConverter

I have a ListBox where I display all order positions. I need to display a price. I created a ValueConverter which takes a OrderPosition object and returns my price as double.
Formula: Amount * Product.Price (Amount and Product are properties in OrderPosition)
My XAML just won't display anything:
<ListBox Grid.Row="1" Grid.Column="0" Margin="3" ItemsSource="{Binding SelectedOrder.OrderPositions}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}x {1}">
<Binding Path="Amount" />
<Binding Path="Product.Label" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Text="{Binding /, Converter={StaticResource PositionPriceConverter}, StringFormat={}{0:c}}" Grid.Column="1"
TextAlignment="Right" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is my converter:
public class PositionPriceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var position = (OrderPosition)value;
return position.Amount * position.Product.Price;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
At the moment you set Path=/ which binds it to CollectionView.CurrentItem
When the source is a collection view, the current item can be specified with a slash (/). For example, the clause Path=/ sets the binding to the current item in the view. When the source is a collection, this syntax specifies the current item of the default collection view.
You can achieve what you're after by setting Path=. or not setting Path altogether.
<TextBlock Text="{Binding Path=., Converter=...}
or
<TextBlock Text="{Binding Converter=...}
but be aware that it will not trigger update when either Amount or Product.Price will change so maybe MultiBinding and IMultiValueConverter would be better option.
I Am not sure if that path you provided to the binding is legal ({Binding /, Converter....).
try to change it in:
<TextBlock Text="{Binding Converter={StaticResource PositionPriceConverter}, StringFormat={}{0:c}}" Grid.Column="1" TextAlignment="Right" />
or
<TextBlock Text="{Binding Path=., Converter={StaticResource PositionPriceConverter}, StringFormat={}{0:c}}" Grid.Column="1" TextAlignment="Right" />
<ListBox Grid.Row="1"
Grid.Column="0"
Margin="3"
ItemsSource="{Binding SelectedOrder.OrderPositions}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}x {1}">
<Binding Path="Amount" />
<Binding Path="Product.Label" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="1">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource PositionPriceConverter}" StringFormat="{}{0}x {1}">
<Binding Path="Amount" />
<Binding Path="Product.Label" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Change the converter like this,
public class PositionPriceConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var amt = (double)values[0];
var price = (double) values[1];
return amt * price;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Categories