Binding color in combobox from code wpf - c#

First of all sorry about my English. I'm generating a datagrid with columns and rows dynamically. Every column I generate this way:
FrameworkElementFactory frameElementFactory =
new FrameworkElementFactory(typeof(ComboBox));
itemsSourceBinding.Source = finalList;
frameElementFactory.SetBinding(ComboBox.ItemsSourceProperty, itemsSourceBinding);
I have a property in the items of finalLsit that has the hexa code of a color. I need to set the background of an item in the combobox with some color depending of that code.
EDIT: I need to do it from code, like setting a binding to the frameElementFactory. I can't do it in the XAML because it is dynamically, maybe I have to create 3 columns and only one with this binding, so I must do it programatically.

Use DataTemplate: You design a template to display your items inside a combobox. For example you design a textlabel to display the color and docked at left of the dropdown menu. You should also have a converter ready to covert color (IValueConverter).
<DataTemplate DataType="{x:Type ComboBoxItem}">
<DockPanel>
<TextBlock Background="{Binding HexaColor}" Width="30" DockPanel.Dock="Left" />
.....
</DockPanel>
</DataTemplate>
Or, you just set the resource to the combobox:
<ComboBox ItemsSource="{Binding finalList}">
<ComboBox.Resources>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Background" Value="{Binding ....}"/>
</Style>
</ComboBox.Resources>
</ComboBox>
Hope this helps

Related

Multi-stage binding from ViewModel to Custom-Control to controls on ControlTemplate not working

I have a MVVM project with a View and a ViewModel in its DataContext.
In this project I have a class ComboBoxCustom which inherits from ComboBox. I define some additional functionality in my ComboBoxCustom class.
To this ComboBoxCustom class I assign a control template to define its appearance.
The (simplified) style defining the (simplified) control template looks like:
<Style TargetType="{x:Type lib:ComboBoxCustom}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
<StackPanel>
<TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>
<ComboBox DataContext="{TemplateBinding DataContext}"
ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedItem="{TemplateBinding SelectedItem}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Which resides in a ResourceDictionary. The real control template has some additional features which are left out since they are not relevant for the question.
I use this ComboBoxCustom control in my View using:
<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
TextPropertyInComboBoxCustom="MyText"
DisplayMemberPath="MyDescription"
SelectedValuePath="MyValue"
SelectedItem="{Binding SelectedMyObject, Mode=TwoWay}"/>
The view is ok, and all items get loaded in the ComboBox which I can select.
The problem is that when I select a different item in the ComboBox, the property SelectedMyObject in my ViewModel does not get updated and consequently its setter is not called. Therefore, the (correct) information about the selected object is not available in my ViewModel.
When I use <ComboBox .../> (without the TextPropertyInComboBoxCustom property) instead of <lib:ComboBoxCustom .../> everything works just fine but then I don't have the additional functionality defined in ComboBoxMessage which I need.
Can anyone tell me what is wrong and how to fix this issue so I can use ComboBoxMessage in my view? Preferably without breaking the MVVM pattern.
Thank you!
Thanx to ASh's comment and information in this post.
The problem is that the TemplateBinding is one way. Therefore, all information from the ViewModel can get into the controls in the template. But not the other way around.
The solution is to specify a normal binding as:
SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Whichs does about the same as a TemplateBinding but is two way.
The control template has become:
<Style TargetType="{x:Type lib:ComboBoxCustom}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
<StackPanel>
<TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>
<ComboBox DataContext="{TemplateBinding DataContext}"
ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am not sure about the SelectedValue property though. With the template like this, it works both when I use the SelectedValue property or the SelectedItem property.
The Mode=TwoWay option can be omitted in the view since the default binding mode for SelectedItem is already two way. The view line becomes:
<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
TextPropertyInComboBoxCustom="MyText"
DisplayMemberPath="MyDescription"
SelectedValuePath="MyValue"
SelectedItem="{Binding SelectedMyObject}"/>
Bind SelectedValue to the property in ViewModel
SelectedValue="{Binding SelectedMyObject, Mode=TwoWay}"
in your lib:ComboBoxCustom

Override ComboBox Styles?

I have a Style in Window.Resources applied to all the ComboBox in the application. However, I have come across a situation where I need the styles in the Window.Resources applied to the ComboBox, but I need to disable one feature I have specifically set in the styles.
I want to be able to enter text into one ComboBox in the application.
Being that the ComboBox text editable part is actually a TextBox I set disabled the TextBox altogether: This style is specifically for the ComboBox style I have,
<Style x:Key="ComboBoxTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="IsEnabled" Value="False" />
<....More setters etc
I have also set in the ComboBox styles IsEditable to false. I had to apply this style to both the TextBox part of the ComboBox to the style work correctly. I have also included the code pointing to the static resource to x:Key="ComboBoxTextBoxStyle" shown above:
<Style TargetType="{x:Type ComboBox}">
<Setter Property="IsEditable" Value="False" />
<...More setter's
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<TextBox Style="{StaticResource ComboBoxTextBoxStyle}"
<...More styles applied here
So now I have a ComboBox that I want to be able to enter text into, so I set the XAML to this,
<ComboBox x:Name="Cmb" IsEditable="True"
ItemsSource="{Binding Source={x:Static viewModels:ShiftManagerViewModel.MyBindingList}}"
SelectedItem="{Binding UpdateSourceTrigger=PropertyChanged, Path=MySeletcProperty}" />
But, the ComboBox is not able to be able to enter text even though I have set the actuall ComboBox to IsEditable="True".
How do I override the styles to allow this one ComboBox enter text?
You would have to create a new Style which will inherit from that Style that already exists and apply it to that particular combox box, style would be like:
A better apporach is that your TextBox element in Style Template for ComboBox IsEnabled property should have binding to ComboBox IsEditable property :
<Style x:Key="ComboBoxTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="IsEnabled"
Value="{Binding Path=IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" />
and then in xaml consuming side you can specify this style :
<ComboBox x:Name="Cmb" IsEditable="True"
ItemsSource="{Binding Source={x:Static viewModels:ShiftManagerViewModel.MyBindingList}}"
SelectedItem="{Binding UpdateSourceTrigger=PropertyChanged, Path=MySeletcProperty}" />

No Style Inheritance Inside ItemsControl / DataTemplate in WPF?

can anyone explain why the TextBlock inside my DataTemplate does not apply the style defined in my UserControl.Resources element, but the second TextBlock ('Test B') does?
I think it may have to do with a dependency property somewhere set to not inherit, but I can't be sure.
<UserControl.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Padding" Value="8 2" />
</Style>
</UserControl.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--Padding does not apply-->
<TextBlock>Test A</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--Padding applies-->
<TextBlock>Test B</TextBlock>
</StackPanel>
Templates are considered as a boundary. Elements within the templates falls in this boundary range, and look up for the style with a matching target type ends within this range at runtime as a result the TextBlock outside will pickup the style and the one inside wont. like adminSoftDK said you should give the style an x:Key and then apply it as static resource it will work.

How do you style a single TextBox among others?

I have a WPF textbox among others that I would like to create a style for.
This style should only apply to this type of textbox.
Here is my textbox:
<TextBox Text="{Binding DeviceInformation.SerialNumber, Mode=OneWay}" BorderThickness="0" Background="Transparent" IsReadOnly="True"/>
My text box basically simulates a label I can copy.
How do I make this into a style and target only this textbox?
If you set Key property on your Style definition, then it will be applied only on TextBoxes that have set this Style. Style={StaticResource=ExampleKey}
Example:
<Window.Resources>
<Style TargetType="TextBox" x:Key="ExampleStyle">
<Setter Property="BorderThickness" Value="0"></Setter>
</Style>
</Window.Resources>
You can also have application-wide styles defined in App.xaml

C#/XAML/WPF binding working partially, only displays first item in List

I have what should be a really simple binding, but the problem I'm seeing is that instead of displaying the three companies (company_list is a List, where Company contains a company_id to bind to), I see the window pop up with only the first company_id in company_list. I have other bindings which seem to work fine, and in some other cases I see that I've used ItemSource instead of DataContext, but when I use that I get "Items collection must be empty before using ItemsSource". I've searched extensively for a simple answer to this in stackoverflow, msdn and elsewhere, and have seen mostly really complex solutions that I haven't been able to understand/apply.
When my window appears, it has:
CompanyA
where it should have:
CompanyA
CompanyB
CompanyC
which is the content of the company_list (yes, verified in debugger). Suggestions appreciated! Code and XAML follow.
ReadMasterCompanyList(); // populates a_state.company_list with 3 companies
// display company list dialog
CompanySelect cs_window = new CompanySelect();
cs_window.CompanyListView.DataContext = a_state.company_list;
// fails: cs_window.CompanyListView.ItemsSource = a_state.company_list;
cs_window.Show();
And the XAML from CompanySelect:
<Grid>
<ListView IsSynchronizedWithCurrentItem="True"
x:Name="CompanyListView"
SelectionMode="Single" SelectionChanged="CompanyListView_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="30"/>
</Style>
</ListView.ItemContainerStyle>
<ListViewItem Content="{Binding Path=company_id}"></ListViewItem>
</ListView>
</Grid>
I would set the ItemsSource of the ListView, rather than the DataContext, either in codebehind:
cs_window.CompanyListView.ItemsSource = a_state.company_list;
or with binding:
<ListView ItemsSource="{Binding company_list}">
And then set the ItemTemplate of the ListView instead.
...
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding company_id}" />
</DataTemplate>
</ListView.ItemTemplate>
...
I would also look into using the MVVM design pattern for testability and separation of concerns, and look at using PascalCase for your property names.
Also, unless you specifically wanted a ListView, I would use a ListBox.
First, set the DataContext only after cs_window.Show().
Second, the ListViewItem you have as a child in your ListView's XAML is why you're only seeing one.
Third, might work better (and would be more MVVM-ish) if you define ItemsSource in the XAML, like this:
<ListView ItemsSource="{Binding Path=company_list}" ...>
That's after making a_state the DataContext of the ListView's container or some other ancestor element.
The problem is, that you define one ListViewItem in your XAML code. You shouldn't do this.
Try something like this:
<Grid>
<ListView IsSynchronizedWithCurrentItem="True"
x:Name="CompanyListView"
SelectionMode="Single" SelectionChanged="CompanyListView_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="30"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Content={Binding Path=company_id}/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

Categories