Always getting DependencyProperty.unsetvalue - wpf

So am testing multibinding in wpf and i have three text boxes which should get the year,month,day and my converter class should return a date with those inputs..pretty simple.
But in my convert method the values[0] is always unset that is i am always getting Dependencyproperty.UnsetValue even if get give it an initial value.
XAML
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:src="clr-namespace:WpfApplication2"
Title="MultiBinding Demo" Width="200" Height="200">
<Window.Resources>
<src:DateConverter x:Key="myConverter" />
</Window.Resources>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
</StackPanel.Resources>
<TextBox Name="tb1" Margin="10" Width="Auto" Height="20"></TextBox>
<TextBox Name="tb2" Margin="10" Width="20" Height="20" ></TextBox>
<TextBox Name="tb3" Width="20" Height="20" ></TextBox>
<Label Name="Date" Width="50" Height="25" Margin="5" >
<Label.Content>
<MultiBinding Converter="{StaticResource myConverter}" Mode="OneWay">
<Binding ElementName="tbl" Path="Text" />
<Binding ElementName="tb2" Path="Text" />
<Binding ElementName="tb3" Path="Text" />
</MultiBinding>
</Label.Content>
</Label>
</StackPanel>
DATECONVERTER CLASS
class DateConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values[0] == DependencyProperty.UnsetValue || values[1] == DependencyProperty.UnsetValue || values[2] == DependencyProperty.UnsetValue)
{
return "";
}
else
{
int year = (int)values[0];
int month = (int)values[1];
int day = (int)values[2];
DateTime date = new DateTime(year, month, day);
return date.ToShortDateString();
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

I'm looking at your XAML, and it looks like your first TextBox is named tb1 (number 1) but in the binding you're referencing element name tbl (letter L).

Related

WPF Binding Calculated Values

I am trying to understand how ValueConverters work. I have three Text Boxes being txtQty, txtPrice and txtAmount representing Qty, Price and Amount respectively and Amount = Qty x Price.
txtQty and txtPrice are unbound controls whilst txtAmount is bound to a DataTable in a DataSet.
How can I update the value in txtAmount which is bound to a DataTable using ValueConveter which takes txtQty and txtPrice as input values?
I can easily achieve this in many ways. But I want to use a ValueConverter for this.
Any ideas?
You can create a converter that implements IMultiValueConverter to compute for your Price and Qty.
public class AmountConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
decimal qty = 0;
decimal price = 0;
if (values?.Length < 2)
throw new ArgumentNullException("Parameter should contain 2 values");
if (!string.IsNullOrEmpty(values[0].ToString()) && !decimal.TryParse(values[0].ToString(), out qty))
throw new ArgumentException("1st value should be decimal.");
if (!string.IsNullOrEmpty(values[1].ToString()) && !decimal.TryParse(values[1].ToString(), out price))
throw new ArgumentException("2nd value should be decimal.");
return (qty * price).ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then use MultiBinding for your Amount textbox
<TextBox x:Name="txtAmount" HorizontalAlignment="Left" IsReadOnly="True">
<TextBox.Text>
<MultiBinding Converter="{StaticResource AmountConverter}">
<Binding ElementName="txtQty" Path="Text" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="txtPrice" Path="Text" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
However you may have to do some interactivity with your txtQty and txtPrice to update your viewmodel-bound Amount, you may also need to invoke a command from your vm to accomplish this.
Listing the entire test xaml and viewmodel code...
<Window x:Class="WpfApp2.MainWindow"
x:Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:interactivity="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
xmlns:vm="clr-namespace:WpfApp2.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:ViewModelTest />
</Window.DataContext>
<Window.Resources>
<local:AmountConverter x:Key="AmountConverter" />
</Window.Resources>
<Grid Margin="12 0 0 0" >
<StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock HorizontalAlignment="Left" Text="Qty" Margin="0 0 12 0" />
<TextBox x:Name="txtQty" HorizontalAlignment="Left" Height="20" Width="50" >
<interactivity:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding DataContext.UpdateAmountCommand, ElementName=root}" CommandParameter="{Binding Path=Text, ElementName=txtAmount}" />
</i:EventTrigger>
</interactivity:Interaction.Triggers>
</TextBox>
</StackPanel>
<StackPanel Orientation="Vertical" >
<TextBlock HorizontalAlignment="Left" Text="Price" Margin="0 0 12 0" />
<TextBox x:Name="txtPrice" HorizontalAlignment="Left" Height="20" Width="50" >
<interactivity:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding DataContext.UpdateAmountCommand, ElementName=root}" CommandParameter="{Binding Path=Text, ElementName=txtAmount}" />
</i:EventTrigger>
</interactivity:Interaction.Triggers>
</TextBox>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock HorizontalAlignment="Left" Text="Amount" Margin="0 0 12 0" />
<TextBox x:Name="txtAmount" HorizontalAlignment="Left" IsReadOnly="True">
<TextBox.Text>
<MultiBinding Converter="{StaticResource AmountConverter}">
<Binding ElementName="txtQty" Path="Text" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="txtPrice" Path="Text" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
</StackPanel>
</Grid>
</Window>
VM
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace WpfApp2.ViewModel
{
public class ViewModelTest : INotifyPropertyChanged
{
public ViewModelTest()
{
UpdateAmountCommand = new CustomCommand<string>(UpdateAmount, (x) => true);
}
private decimal _amount;
public decimal Amount
{
get => _amount;
set
{
if (_amount != value)
{
_amount = value;
OnPropertyChanged();
}
}
}
public CustomCommand<string> UpdateAmountCommand { get; }
private void UpdateAmount(string amountText)
{
Amount = decimal.Parse(amountText);
Debug.WriteLine(Amount);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Not sure if there's an easier way, this is just on top of my head.
PS: You can copy the CustomCommand implementation here.
Hope this helps.

How do I obtain the Index of a ListViewItem?

I have a list view that uses a collectionsource and wish to set the background colour of the ListViewItems depending upon whether their index in the list matches a value held in the view model. To do this I am binding the background of the listVeiwItem to the value in the ViewModel, and using a converter to determine the background colour. To make this determination the converter needs to be passed the ListViewItem's index. How do I obtain the index using XAML?
Here is the XAML for the data template used by the ListView:
<DataTemplate x:Key="ILMemberTemplate">
<StackPanel Orientation="Horizontal" Background="{Binding Path=ListIndex, Mode=OneWay, Converter={StaticResource ParticipantBackground}, ConverterParameter={???}}">
<TextBlock
Width="200"
TextAlignment="Left"
Foreground="{Binding Path=IsPC, Mode=OneWay, Converter={StaticResource ParticipantColour}}"
Text="{Binding Path=Name, Mode=OneWay}"/>
<TextBlock
Width="40"
TextAlignment="Center"
Foreground="{Binding Path=IsPC, Mode=OneWay, Converter={StaticResource ParticipantColour}}"
Text="{Binding Path=Initiative, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
You can't obtain the index in XAML and pass it as a ConverterParameter. This is not possible. But you can use a multi converter and bind to both the ListIndex and the parent ListViewItem container, e.g.:
class MultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var listIndex = values[0];
//...
ListViewItem item = value as ListViewItem;
ListView lv = FindParent<ListView>(item);
ICollectionView view = lv.ItemsSource as ICollectionView;
IEnumerator e = view.GetEnumerator();
int index = 0;
while (e.MoveNext())
{
if (Equals(item.DataContext, e.Current))
return index;
else
index++;
}
//return some brush based on the indexes..
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
private static T FindParent<T>(DependencyObject dependencyObject) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null) return null;
var parentT = parent as T;
return parentT ?? FindParent<T>(parent);
}
}
XAML:
<DataTemplate x:Key="ILMemberTemplate">
<StackPanel Orientation="Horizontal">
<StackPanel.Background>
<MultiBinding Converter="{StaticResource multiConverter}">
<Binding Path="ListIndex" />
<Binding Path="." RelativeSource="{RelativeSource AncestorType=ListViewItem}" />
</MultiBinding>
</StackPanel.Background>
<!-- ... -->
</StackPanel>
</DataTemplate>

wpf specify multiple DataBindings in Groupbox IsEnabled Property

I have a GroupBox whose IsEnabled Property is set via a property on the ViewModel as under:-
<GroupBox>
<Canvas IsEnabled="{Binding CurrentRec.Current_Selected_Category.NoBonus,Converter={StaticResource TFC}}">
<Label Content="Amount:" Width="55" Canvas.Left="9" Canvas.Top="-2"/>
<TextBox x:Name="txtBonusAmount" Width="76" Canvas.Left="12" Canvas.Top="20" Text="Some text"/>
<Label Content="Bonus:" Canvas.Top="38" Width="54" Canvas.Left="10"/>
<TextBox x:Name="txtBonus" Width="76" Canvas.Left="13" Canvas.Top="58" Text="Some Text"/>
</Canvas>
<Groupbox>
There are more than one properties in my viewmodel affecting the IsEnabled property of Canvas.How do i specify those additional properties against the IsEnabled property of Canvas?
Use a MultiBinding with a converter:
<GroupBox>
<GroupBox.Resources>
<local:MultiConverter x:Key="conv" />
</GroupBox.Resources>
<Canvas>
<Canvas.IsEnabled>
<MultiBinding Converter="{StaticResource conv}">
<Binding Path="CurrentRec.Current_Selected_Category.NoBonus" />
<Binding Path="TheOtherProperty" />
</MultiBinding>
</Canvas.IsEnabled>
<Label Content="Amount:" Width="55" Canvas.Left="9" Canvas.Top="-2"/>
<TextBox x:Name="txtBonusAmount" Width="76" Canvas.Left="12" Canvas.Top="20" Text="Some text"/>
<Label Content="Bonus:" Canvas.Top="38" Width="54" Canvas.Left="10"/>
<TextBox x:Name="txtBonus" Width="76" Canvas.Left="13" Canvas.Top="58" Text="Some Text"/>
</Canvas>
</GroupBox>
The converter class should implement the IMultiValueConverter interface and return a bool from the Convert method:
public class MultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
bool noBonus = System.Convert.ToBoolean(values[0]);
bool theOtherSourceProperty = System.Convert.ToBoolean(values[1]);
//..
return noBonus && theOtherSourceProperty;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

Add value ticks into my Circular Slider

This is my Circular slider:
<Slider Name="knobSlider" Minimum="0" Maximum="50" Value="1" Height="99" Width="75"
HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1">
<Slider.Template>
<ControlTemplate>
<Viewbox>
<Canvas Width="300" Height="300" Margin="5">
<Ellipse Fill="Transparent" Width="300" Height="300" Canvas.Left="0" Canvas.Top="0"
Stroke="#FF878889" StrokeThickness="10"
MouseLeftButtonUp="Ellipse_MouseLeftButtonUp"
MouseMove="Ellipse_MouseMove"/>
<Ellipse Fill="Transparent" Width="60" Height="60" Canvas.Left="120" Canvas.Top="120"/>
<Canvas>
<Line Stroke="Transparent" StrokeThickness="5" X1="150" Y1="150" X2="150" Y2="10"
MouseLeftButtonUp="Ellipse_MouseLeftButtonUp"/>
<Ellipse Fill="White" Width="30" Height="30" Canvas.Left="140" Canvas.Top="0"
MouseLeftButtonDown="Ellipse_MouseLeftButtonDown"
MouseLeftButtonUp="Ellipse_MouseLeftButtonUp">
<Ellipse.ToolTip>
<ToolTip>
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="Value" Converter="{StaticResource valueTextConverter}"/>
</ToolTip>
</Ellipse.ToolTip>
</Ellipse>
<Canvas.RenderTransform>
<RotateTransform CenterX="150" CenterY="150">
<RotateTransform.Angle>
<MultiBinding Converter="{StaticResource valueAngleConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Minimum"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Maximum"/>
</MultiBinding>
</RotateTransform.Angle>
</RotateTransform>
</Canvas.RenderTransform>
</Canvas>
</Canvas>
</Viewbox>
</ControlTemplate>
</Slider.Template>
</Slider>
public class ValueAngleConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
double value = (double)values[0];
double minimum = (double)values[1];
double maximum = (double)values[2];
return MyHelper.GetAngle(value, maximum, minimum);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
public class ValueTextConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
double v = (double)value;
return String.Format("{0:F2}", v);
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
And the results:
Is it possible to change the look of this Circular Slider to something similar to this one:
And add value tick and show the current value of this controller ad the button of this control like in the example instead of put a textBlock inside the Circle ?

WPF DataGrid ClipboardBinding Multibinding possible?

is there a solution to bind multiple properties to my ClipboardBinding.
I tried the following code but this didnt work:
<DataGridTemplateColumn CanUserSort="True" SortMemberPath="Characteristic.Area.Name.ActualTranslation" MinWidth="120" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5,0,5,0">
<TextBlock Text="{Binding Characteristic.Area.Name.ActualTranslation}"></TextBlock>
<TextBlock Text=" "></TextBlock>
<TextBlock Text="{Binding AreaItem.Value}"></TextBlock>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{lex:Loc Area}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.ClipboardContentBinding>
<!-- TODO: ClipboardBinding Area -->
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="Characteristic.Area.Name.ActualTranslation" />
<Binding Path="AreaItem.Value" />
</MultiBinding>
</DataGridTemplateColumn.ClipboardContentBinding>
</DataGridTemplateColumn>
i'd appreciate adivce for a workaround too.
Please help
You should use converter (msdn).
class StringFormatConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return string.Format(parameter.ToString(), values);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML:
<DataGridTemplateColumn.ClipboardContentBinding>
<MultiBinding
ConverterParameter=" {0} {1}"
Converter="{StaticResource conString}">
<Binding Path="Characteristic.Area.Name.ActualTranslation" />
<Binding Path="AreaItem.Value" />
</MultiBinding>
</DataGridTemplateColumn.ClipboardContentBinding>

Resources