How do I bind ARGB values to a rectangle fill property in a combo box item? - wpf

I have a combobox of custom objects each of which is displayed as a colored rectangle and beside it a text.
I have the ARGB values I want to display from the object, but I want to convert that to a color and set it to the fill property of the rectangle. So far I have the following based on this post
Binding R G B properties of color in wpf
How do you specify which propertied in SubCategory to pass to the value converter? And how to set the result of the converter to the Fill property of the rectangle?
//XAML
<Controls:MetroWindow.Resources>
<local:ArgbConverter x:Key="argbConverter"></local:ArgbConverter>
</Controls:MetroWindow.Resources>
<ComboBox ItemsSource="{Binding Path=SubCategories}" HorizontalAlignment="Stretch" SelectedItem="{Binding Path=SelectedSubCategory, Mode=TwoWay}" IsEditable="False" TextBoxBase.TextChanged="SubCategory_ComboBox_TextChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="Aqua" Width="16" Height="16" Margin="0,2,5,2" ></Rectangle>
<TextBlock Text="{Binding Path=name, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
//Value Converter
public class ArgbConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var a = System.Convert.ToByte(values[0]);
var r = System.Convert.ToByte(values[1]);
var g = System.Convert.ToByte(values[2]);
var b = System.Convert.ToByte(values[3]);
return new Color() { A = a, B = b, R = r, G = g };
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
//VIEWMODEL
public ObservableCollection<SubCategory> SubCategories { get; set; } = new ObservableCollection<SubCategory>() { new SubCategory() { name = "person" } };
//SubCategory class
public class SubCategory
{
//default values - each of these objects will have different argb values
public int A { get; set; } = 95;
public int R { get; set; } = 225;
public int G { get; set; } = 80;
public int B { get; set; } = 25;
public string name { get; set; }
}

Why can't you use IValueConverter instead of IMultiValueConverter?
XAML
<Rectangle Fill="{Binding Path=., Converter={StaticResource argbConverter}}"
Width="16" Height="16"/>
Converter
public class ArgbConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var item = value as SubCategory;
var a = System.Convert.ToByte(item.A);
var r = System.Convert.ToByte(item.R);
var g = System.Convert.ToByte(item.G);
var b = System.Convert.ToByte(item.B);
return new SolidColorBrush(new Color() { A = a, B = b, R = r, G = g });
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

You have already implemented an IMultiValueConverter. So the obvious answer seems to be a MultiBinding, with four Bindings to the A, R, G and B properties:
<Rectangle>
<Rectangle.Fill>
<SolidColorBrush>
<SolidColorBrush.Color>
<MultiBinding Converter="{StaticResource argbConverter}">
<Binding Path="A"/>
<Binding Path="R"/>
<Binding Path="G"/>
<Binding Path="B"/>
</MultiBinding>
</SolidColorBrush.Color>
</SolidColorBrush>
</Rectangle.Fill>
</Rectangle>

Related

wpf combobox multibinding could not get combobox items (programmatically added or bind to database) of relative source combobox in first step

I want to disable some items of combobox by specific conditions. For this issue, I used multibinding.
If I described all items of combobox in xaml, there is no problem. But I want to populate combobox items programmatically. So in this case , I could not get items, returns null, and throw me out of the program in the first step.
my xaml codes are like that:
<Window.Resources>
<local:TekerDisabler x:Key="tekerDisabler"/>
</Window.Resources>
<Grid>
<ComboBox x:Name="cbx" HorizontalAlignment="Left" Margin="41,125,0,0" VerticalAlignment="Top" Width="227">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnabled">
<Setter.Value>
<MultiBinding Converter="{StaticResource tekerDisabler}">
<Binding ElementName="txt1" Path="Text"/>
<Binding ElementName="txt2" Path="Text"/>
<Binding ElementName="txt3" Path="Text"/>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<TextBox x:Name="txt1" HorizontalAlignment="Left" Height="23" Margin="41,38,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="txt2" HorizontalAlignment="Left" Height="23" Margin="207,38,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="211" TextChanged="txt2_TextChanged"/>
<TextBox x:Name="txt3" HorizontalAlignment="Left" Height="24" Margin="478,37,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="196"/>
</Grid>
and my c# codes are like that:
namespace App1.Pencereler
{
public partial class deneme : Window
{
public deneme()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
cbx.Items.Add("0");
cbx.Items.Add("1");
cbx.Items.Add("2");
cbx.Items.Add("3");
}
private void txt2_TextChanged(object sender, TextChangedEventArgs e)
{
cbx.SelectedIndex = 1;
}
}
class TekerDisabler : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
bool enable = true;
var itemler = values[3] as ComboBoxItem;
if (itemler == null || values[0].ToString() == null || values[1].ToString() == null || values[2].ToString() == null)
{ enable = true; }
else
{
switch (values[0].ToString())
{
case "a":
switch (values[1].ToString())
{
case "b":
switch (values[2].ToString())
{
case "c":
switch (itemler.Content.ToString())
{
case "0":
case "2":
enable = false;
break;
default:
enable = true;
break;
}
break;
default:
enable = true;
break;
}
break;
default:
enable = true;
break;
}
break;
default:
enable = true;
break;
}
}
return enable;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
For Example, in the first step, I write txt1:a, txt2:b, txt3:d and so all items' display enabled, and then I write txt1:a, txt2:b, txt3:c and contents of combobox (0,2) disabled, there is no problem. But when running program, in the first step I write txt1:a, txt2:b, txt3:c when drop down combobox, program trow me out.
How to overcome this problem?
Error Message and details are like that:
And Error details are like that:
System.NullReferenceException
HResult=0x80004003
İleti=Nesne başvurusu bir nesnenin örneğine ayarlanmadı.
Kaynak=App1
StackTrace:
konum App1.Pencereler.TekerDisabler.Convert(Object[] values, Type targetType, Object parameter, CultureInfo culture) D:\C Sharp\WPF\App1\App1\Pencereler\deneme.xaml.cs içinde: 60. satır
konum System.Windows.Data.MultiBindingExpression.TransferValue()
konum System.Windows.Data.MultiBindingExpression.Transfer()
konum System.Windows.Data.MultiBindingExpression.UpdateTarget(Boolean includeInnerBindings)
konum System.Windows.Data.MultiBindingExpression.AttachToContext(Boolean lastChance)
konum System.Windows.Data.MultiBindingExpression.AttachOverride(DependencyObject d, DependencyProperty dp)
konum System.Windows.Data.BindingExpressionBase.OnAttach(DependencyObject d, DependencyProperty dp)
It would be interesting to know what exact error you get.
I assume the ComboBoxItem.Content returns null. The item containers are rendered (generated) after the ComboBox is opened. Only the data items exist at this moment. So opening the drop down the first time all item containers are null and about to be rendered.
Anyway, the following simplified version of your code will very likely fix your problem:
TekerDisabler.cs
public class TekerDisabler : IMultiValueConverter
{
#region Implementation of IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var currentItem = values[0] as string;
var predicate = "abc";
string input = string.Concat(values.Skip(1).Cast<string>());
return !(input.Equals(predicate, StringComparison.Ordinal)
&& (currentItem.Equals("0", StringComparison.Ordinal)
|| currentItem.Equals("2", StringComparison.Ordinal)));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
throw new NotSupportedException();
#endregion
}
TekerDisabler.cs - Alternative version
public class TekerDisabler : IMultiValueConverter
{
#region Implementation of IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var currentItem = values[0] as string;
var predicate = "abc";
string input = string.Concat(values.Skip(1).Cast<string>());
return !(values[1].Equals("a", StringComparison.Ordinal)
&& values[2].Equals("b", StringComparison.Ordinal)
&& values[3].Equals("c", StringComparison.Ordinal)
&& (currentItem.Equals("0", StringComparison.Ordinal)
|| currentItem.Equals("2", StringComparison.Ordinal)));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
throw new NotSupportedException();
#endregion
}
MainWindow.xamlk.cs
partial class MainWIndow : Window
{
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
"Items",
typeof(ObservableCollection<string>),
typeof(MainWindow),
new PropertyMetadata(default(ObservableCollection<string>)));
public ObservableCollection<string> Items
{
get => (ObservableCollection<string>) GetValue(MainWindow.ResultsProperty);
set => SetValue(MainWindow.ResultsProperty, value);
}
}
MainWindow.xaml
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=main:MainWindow}, Path=Items}">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnabled">
<Setter.Value>
<MultiBinding Converter="{StaticResource CellForegroundMultiValueConverter}">
<Binding />
<Binding ElementName="TextBox1" Path="Text" />
<Binding ElementName="TextBox2" Path="Text" />
<Binding ElementName="TextBox3" Path="Text" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<TextBox x:Name="TextBox1" />
<TextBox x:Name="TextBox2" />
<TextBox x:Name="TextBox3" />
I accomlished to overcome my problem by adding
cbx.IsDropDownOpen = true;
cbx.IsDropDownOpen = false;
after
cbx.Items.Add("0");
cbx.Items.Add("1");
cbx.Items.Add("2");
cbx.Items.Add("3");
in 'Window_Loaded' event.

Can WPF Databinding source include a parameter?

Can I pass an index number into this list (SomeList)?
FontSize="{Binding FontSize, Source={x:Static ut:ViewSetupData.SomeList}, FallbackValue=12}"
You can put a constant indexer in the Path:
{Binding Path=[(sys:Int32)0], Source={x:Static ut:ViewSetupData.SomeList}}
But you can't bind a property of a Binding, so there's no way to stuff a parameter in there. However, you can combine multiple bindings in a MultiBinding, so you could use one of those with a multi-value converter:
C#:
public class IListIndexerConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// You might want a little more error-checking than this...
return ((IList)values[0])[(int)values[1]];
}
public virtual object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
XAML:
<TextBlock>
<TextBlock.Resources>
<local:IListIndexerConverter x:Key="ListIndexer" />
</TextBlock.Resources>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource ListIndexer}">
<Binding Source="{x:Static ut:ViewSetupData.SomeList}" />
<Binding
ElementName="MyComboBox"
Path="SelectedIndex"
/>
</MultiBinding>
</TextBlock.Test>
</TextBlock>
Update
While you were marking this as the solution, I was writing a more complete solution that addressed your need to grab a property from the list item:
C#:
public class ListItemPropertyGetter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
try
{
var list = values[0] as IList;
var index = (int)(values[1] ?? 0);
var propname = values[2] as String;
object item = list[index];
var prop = item.GetType().GetProperty(propname);
var propvalue = prop.GetValue(item);
return propvalue;
}
catch
{
return null;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
// Gotta put these somewhere
public static List<FontSizeThing> FontSizeThings { get; } =
new List<FontSizeThing>
{
new FontSizeThing(10),
new FontSizeThing(10.5),
new FontSizeThing(11),
new FontSizeThing(12),
new FontSizeThing(14),
new FontSizeThing(15),
};
}
public class FontSizeThing {
public FontSizeThing(double n) { FontSize = n; }
public double FontSize { get; set; }
}
XAML:
<ComboBox x:Name="FontSizeOptionCombo">
<sys:Int32>0</sys:Int32>
<sys:Int32>1</sys:Int32>
<sys:Int32>2</sys:Int32>
<sys:Int32>3</sys:Int32>
<sys:Int32>4</sys:Int32>
</ComboBox>
<TextBlock Text="Testing">
<TextBlock.Resources>
<hconv:ListItemPropertyGetter x:Key="ListItemPropertyGetter" />
</TextBlock.Resources>
<TextBlock.FontSize>
<MultiBinding Converter="{StaticResource ListItemPropertyGetter}" StringFormat="{}{0}">
<Binding Source="{x:Static hconv:ListItemPropertyGetter.FontSizeThings}" />
<Binding ElementName="FontSizeOptionCombo" Path="SelectedItem" />
<Binding Source="FontSize" />
</MultiBinding>
</TextBlock.FontSize>
</TextBlock>
FINAL UPDATE
Note that if I had merely populated FontSizeOptionCombo with the FontThings themselves, I could very simply have bound like this:
<ComboBox
x:Name="OtherCombo"
ItemsSource="{x:Static hconv:ListItemPropertyGetter.FontSizeThings}"
DisplayMemberPath="FontSize"
FontSize="{Binding SelectedItem.FontSize, ElementName=OtherCombo, FallbackValue=20}"
/>
If that fits in with what you're doing, it's by far the nicest way.

Set selected item or selected index for listbox contains binded items source WPF

I'm newbie on WPF. I didn't success to set selected index\selected value to list box of images items.
My current code is:
On xaml:
<ListBox Name="symbolSrc" Grid.Row="1" Width="485" Height="100" HorizontalAlignment="Left" Margin="7,-35,0,0" ItemTemplate="{StaticResource cmbTemplate}" IsSynchronizedWithCurrentItem="True" SelectedValuePath="photo">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="6 0 0 0" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsSource>
<MultiBinding Converter="{StaticResource SymbolComboboxItemsFilter}">
<Binding ElementName ="RadioButLR" Path="IsChecked" Mode="OneWay"/>
<Binding ElementName ="RadioButHR" Path="IsChecked" Mode="OneWay"/>
</MultiBinding>
</ListBox.ItemsSource>
<ListBox.SelectedValue>
<MultiBinding Converter="{StaticResource SymbolComboboxSelectedItem}">
<Binding ElementName ="RadioButLR" Path="IsChecked" Mode="OneWay"/>
<Binding ElementName ="RadioButHR" Path="IsChecked" Mode="OneWay"/>
</MultiBinding>
</ListBox.SelectedValue>
</ListBox>
On xaml.cs:
public class SymbolComboboxItemsFilterConverter: IMultiValueConverter
{
public class symbolCmbBoxItem
{
public string photo { get; set; }
public string photoName { get; set; }
}
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
List<symbolCmbBoxItem> itemsList = new List<symbolCmbBoxItem>();
string targetDir = System.Windows.Forms.Application.ExecutablePath + "\\..\\..\\..\\INIT\\IC";
string[] filesArray = Directory.GetFiles(targetDir, "*", SearchOption.TopDirectoryOnly);
if ((bool)values[0])
{
foreach (string file in filesArray.Where(s => s.Contains("_LR")))
{
itemsList.Add(new symbolCmbBoxItem { photo = file, photoName = "" });
}
}
else
{
foreach (string file in filesArray.Where(s => s.Contains("_HR")))
{
itemsList.Add(new symbolCmbBoxItem { photo = file, photoName = "" });
}
}
return itemsList;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class SymbolComboboxItemsSelectedIndex : IMultiValueConverter
{
public class symbolCmbBoxItem
{
public string photo { get; set; }
public string photoName { get; set; }
}
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
List<symbolCmbBoxItem> itemsList = new List<symbolCmbBoxItem>();
string targetDir = System.Windows.Forms.Application.ExecutablePath + "\\..\\..\\..\\INIT\\IC";
string[] filesArray = Directory.GetFiles(targetDir, "*", SearchOption.TopDirectoryOnly);
if ((bool)values[0])
{
string item = filesArray.Where(s => s.Contains("_LR")).First();
return new symbolCmbBoxItem { photo = item, photoName = "" };
}
else
{
string item = filesArray.Where(s => s.Contains("_HR")).First();
return new symbolCmbBoxItem { photo = item, photoName = "" };
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I also tried set the selected item by using:
SelectedItem="1"
And it didn't work.
Also tried to using the same multi converter at above and set always the selected item to "1" and it also didn't work.
(In order to set listbox of images I used by below reference:
http://www.codescratcher.com/wpf/wpf-combobox-with-image/ )
Any ideas please?
Your help is very appreciated!

Silverlight datagrid cell edit does not reflect in cell

In this silverlight grid cell, the display value does not update when the edit value is updated, any ideas why?
<sdk:DataGridTemplateColumn Header="Bid Qty" IsReadOnly="True">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{Binding Path=BidPrice.Quantity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource CustomDoubleToStringConverter}}"
Foreground="{Binding Path=BidPrice.TextColour}"
ToolTipService.ToolTip="{Binding Path=BidPrice.ToolTip}"
ToolTipService.Placement="Right" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=BidPrice.Quantity, Mode=TwoWay, Converter={StaticResource CustomDoubleToStringConverter}}" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
Code (very simplified) :
// field is bound to BidPrice on this
public class PriceStackLine : INotifyPropertyChanged
{
public ProductPrice BidPrice { get; set; }
}
// BidPrice is a ProductPrice with a Quantity property
public class ProductPrice : INotifyPropertyChanged
{
private double _qty;
public double Quantity
{
get
{
return _qty;
}
set
{
_qty = value;
NotifyPropertyChanged("Quantity");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// converter
public class CustomDoubleToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double num = (double)value;
if (num == 0)
return string.Empty;
return num;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
double result = 0;
double.TryParse(value.ToString(), out result);
return result;
}
}
UPDATE - FIXED! The TextColour was null on the new edits which was probably defaulting to transparent, that is why I couldnt see the edit

Changing element height depending on a selection of combobox

I have a combobox for selecting media types. I would like mediaelement's height to change when .vmw, .mpeg or .avi files are selected. How can I achieve this with MVVM approach?
Thanks in advance
You could bind the Width and Height of the MediaElement directly to its Source property with an appropriate converter, which selects the proper size depending on the media type:
<MediaElement
Width="{Binding Path=Source, RelativeSource={RelativeSource Self}, Converter={StaticResource MediaElementSizeConverter}, ConverterParameter=Width}"
Height="{Binding Path=Source, RelativeSource={RelativeSource Self}, Converter={StaticResource MediaElementSizeConverter}, ConverterParameter=Height}"/>
The converter:
public class MediaElementSizeConverter : IValueConverter
{
private const double defaultWidth = 320d;
private const double defaultHeight = 240d;
private const double wmvWidth = 640d;
private const double wmvHeight = 480d;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Uri source = value as Uri;
if (source != null)
{
if (source.AbsolutePath.EndsWith(".wmv"))
{
return (parameter as string) == "Width" ? wmvWidth : wmvHeight;
}
// more media types ...
}
return (parameter as string) == "Width" ? defaultWidth : defaultHeight;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
One solution would be to bind the ComboBox to a list of self created MediaTypeDefinition classes.
public class MediaTypeDefinition
{
public string Name { get; set; }
public int Height { get; set; }
}
You can then bind the SelectedItem to the height of the media element.
<ComboBox x:Name="mediaTypeList" ItemsSource="{Binding Definitions}" SelectedValuePath="Name" />
<MediaElement Height="{Binding SelectedItem.Height, Elementname=mediaTypeList}" />

Resources