Editable DatagridComboBoxColumn in WPF using C# - c#

I'm using WPF DataGrid control with dynamic columns binding at run time.(DataGrid columns are dynamic)
Sample code is as below
.xaml is having below code
<Style TargetType="ComboBox" x:Key="ComboBoxEditingStyle">
<Setter Property="ItemsSource" Value="{Binding Path=DefinedFormatters}" />
<Setter Property="IsDropDownOpen" Value="False" />
<Setter Property="IsEditable" Value="True" />
<Setter Property="SelectedValue" Value="Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
.xaml.cs file is having below code,
Binding theBinding = new Binding();
theBinding.Mode = BindingMode.TwoWay;
theBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
theBinding.ValidatesOnDataErrors = true;
DataGridComboBoxColumn colSuggestionList = new DataGridComboBoxColumn();
// theCollection is Collection<string>
colSuggestionList.ItemsSource = theCollection;
colSuggestionList.SelectedValueBinding = theBinding;
colSuggestionList.Visibility = Visibility.Visible;
colSuggestionList.EditingElementStyle = dgMainTemplate.FindResource("ComboBoxEditingStyle") as Style;
// dgMainTemplate is wpf DataGrid
dgMainTemplate.Columns.Add(colSuggestionList);
Column added properly, but I want to make this column as editable. User should be able to select either existing item from available list or enter a new value which is not exists in available list.
Here EditingElementStyle will add editable combobox but items are not showing in combobox until user selects any item.

Finally I got solution to this problem, Modified code is as below,
<Style x:Key="TextBlockComboBoxStyle" TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Label Content="{TemplateBinding Text}" Style="{StaticResource {x:Type Label}}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ComboBox" x:Key="ComboBoxEditingStyle">
<Setter Property="ItemsSource" Value="{Binding Path=DefinedFormatters}" />
<Setter Property="IsDropDownOpen" Value="False" />
<Setter Property="IsEditable" Value="True" />
</Style>
.cs code is,
Binding theBinding = new Binding();
theBinding.Mode = BindingMode.TwoWay;
theBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
theBinding.ValidatesOnDataErrors = true;
DataGridComboBoxColumn colSuggestionList = new DataGridComboBoxColumn();
// theCollection is Collection<string>
colSuggestionList.ItemsSource = theCollection;
colSuggestionList.SelectedValueBinding = theBinding;
colSuggestionList.Visibility = Visibility.Visible;
colSuggestionList.EditingElementStyle = dgMainTemplate.FindResource("ComboBoxEditingStyle") as Style;
colSuggestionList.EditingElementStyle = dgMainTemplate.FindResource("ComboBoxEditingStyle") as Style;
colSuggestionList.ElementStyle = dgMainTemplate.FindResource("TextBlockComboBoxStyle") as Style;
// dgMainTemplate is wpf DataGrid
dgMainTemplate.Columns.Add(colSuggestionList);

Related

WPF Combobox binding issue with object

I have found several example of wpf combobox binding but when trying to adapt it I don't get the expected results. What appears in the combobox instead of the value is this:
CuttingProblemHelper.Models.ComboBoxPairs
Can please anyone help?
Example of data in the SQL table:
Id CutProbCategId Problem
1 1 Brins coupés
2 1 Brins grafignés
3 1 Fil non dégainé
What I try to acheive is having a key, value pair for each entry in the combobox from an sql table.
So I created a class to store key, value pair:
namespace CuttingProblemHelper.Models {
public class ComboBoxPairs
{
public int _Key { get; set; }
public string _Value { get; set; }
public ComboBoxPairs(int _key, string _value)
{
_Key = _key;
_Value = _value;
}
} }
Next get the data from the table and add them to the object ComboBoxPairs
private void AddProblemCategtoCombobox(int categ)
{
// erase list
cmbxProblem.ItemsSource = null;
// get list
DataRow[] CutProblemsRows = gediDataSet.CutProblems.Select("CutProbCategId= " + categ);
// we have rows
if (CutProblemsRows != null)
{
// initialize object ComboBoxPairs
List<ComboBoxPairs> cbp = new List<ComboBoxPairs>();
foreach (DataRow row in CutProblemsRows)
{
// add id and problem key, value pair
cbp.Add(new ComboBoxPairs(int.Parse(row["Id"].ToString()), row["Problem"].ToString()));
}
// define properties of combobox
cmbxProblem.DisplayMemberPath = "_Value";
cmbxProblem.SelectedValuePath = "_Key";
// bind comboboxpairs list
cmbxProblem.ItemsSource = cbp.ToList();
}
}
Here's the XAML of the combobox and formatting of the display of items:
<ComboBox x:Name="cmbxProblem" Grid.Column="1" Margin="10,10,10,10" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionChanged="cmbxProblem_SelectionChanged" FontSize="40" ItemsSource="{Binding Collection}" Grid.ColumnSpan="2">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Label Name="lbl" Content="{Binding}" ></Label>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="lbl" Property="FontStyle" Value="Normal"></Setter>
<Setter TargetName="lbl" Property="Background" Value="AliceBlue"></Setter>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="lbl" Property="FontStyle" Value="Italic"></Setter>
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="lbl" Property="FontStyle" Value="Normal"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
Thank you very much!
I found a solution but the combobox does not only display the value but it displays the paired key and value. But when a value is selected it displays only the selected value. Anyone knows what to change to show only the value without the key?
See pictures to better understand.
Here's the solution I came up with after searching several combobox solutions:
In the XAML
<ComboBox x:Name="cmbxProblem" Grid.Column="1" Margin="10,10,10,10" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionChanged="cmbxProblem_SelectionChanged" FontSize="40" ItemsSource="{Binding MyCollection}" DisplayMemberPath="_Value" SelectedValuePath="_Key" Grid.ColumnSpan="2">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Label Name="lbl" Content="{Binding}" ></Label>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="lbl" Property="FontStyle" Value="Normal"></Setter>
<Setter TargetName="lbl" Property="Background" Value="AliceBlue"></Setter>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="lbl" Property="FontStyle" Value="Italic"></Setter>
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="lbl" Property="FontStyle" Value="Normal"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
In the code behind:
private void AddProblemCategtoCombobox(int categ)
{
// delete list
cmbxProblem.ItemsSource = null;
// get items for list
DataRow[] CutProblemsRows = gediDataSet.CutProblems.Select("CutProbCategId= " + categ);
// we have items
if (CutProblemsRows != null)
{
// create collection
MyCollection = new ObservableCollection<KeyValuePair<int, string>>();
foreach (DataRow row in CutProblemsRows)
{
// fill collection with items
MyCollection.Add(new KeyValuePair<int, string>(int.Parse(row["Id"].ToString()), row["Problem"].ToString()));
}
// define properties for the combobox
cmbxProblem.DisplayMemberPath = "Value";
cmbxProblem.SelectedValuePath = "Key";
cmbxProblem.ItemsSource = MyCollection;
}
}

After change wpf button IsMouseOver trigger color not work

this is my button code
public class MetroButton : Button
{
public static readonly DependencyProperty MoseOverBrushProperty;
public static readonly DependencyProperty PressedBrushProperty;
public MetroButton():base()
{
var resource = new ResourceDictionary
{
Source = new Uri("/Parking.Component.Ui;component/Styles/ButtonMetro.xaml",
UriKind.RelativeOrAbsolute)
};
Style = resource["ButtonMetro"] as Style;
//SetResourceReference(StyleProperty, Style);
}
static MetroButton()
{
MoseOverBrushProperty = DependencyProperty.Register("MoseOverBrush", typeof(Brush), typeof(MetroButton));
PressedBrushProperty = DependencyProperty.Register("PressedBrush", typeof(Brush), typeof(MetroButton));
}
public Brush MoseOverBrush
{
get { return (Brush)base.GetValue(MoseOverBrushProperty); }
set { base.SetValue(MoseOverBrushProperty, value); }
}
public Brush PressedBrush
{
get { return (Brush)base.GetValue(PressedBrushProperty); }
set { base.SetValue(PressedBrushProperty, value); }
}
}
and I use this style for my button
<Style x:Key="ButtonMetro" TargetType="{ x:Type LochalUI:MetroButton}">
<Setter Property="Foreground" Value="White" />
<Setter Property="MoseOverBrush" Value="#FF3F62FD"/>
<Setter Property="PressedBrush" Value="#FF000099"/>
<Setter Property="Background" Value="#FF6B9AFF"/>
<Setter Property="FontSize" Value="15" />
<Setter Property="FontFamily" Value="B Yekan" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type LochalUI:MetroButton}">
<Border x:Name="border" CornerRadius="4" Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource Self}}" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{Binding Path=PressedBrush , RelativeSource={RelativeSource Self}}" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
but the problem is there when i put color for my button background like below code:
<UI:MetroButton HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="132" Height="107" Background="#FF09CD00" >
<Grid>
<Label Content="تنظیمات" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="5,5,5,12" Foreground="White" Margin="5"/>
</Grid>
</UI:MetroButton>
the IsMouseOver changer color and IsPressed Triggers not work.
(I don't want use static resource in my setters)
Changing other properties has no effect just changing background made this problem.
I found the answer problem was in 2 place:
first one when we use trigger in
<ControlTemplate.Triggers/>
you have sure you set your setter set property on the currect object
and the secend one is in the binding we have change
Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource Self}}"
to
Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource TemplatedParent}}"
because MoseOverBrush is parrent property not the ControlTemplate property

Dynamically Create TextBlock and TextBox in XAML WPF

My Question Is I have a grid where number of TextBlock and TextBox will vary as per the ComboBox SelectedItem Change
So, what I want is that I have written a function for get the details of the TextBlock and TextBox.
C# Function
public void GetAdditionalAttributes()
{
using (Entities _entities = new Entities())
{
var attributeAll = (from c in _entities.AdditionalAttributeValues
where c.DeviceID == 35
select new AttributesClass { AttributeValue = c.AdditionalAttributeValue1, AttributeName = c.AdditionalAttribute.Name }).ToList();
DeviceAttributes = new ObservableCollection<AttributesClass>(attributeAll);
}
}
Now In the XAML I was trying:
<Style x:Key="AdditionalAttributeDisplay"
TargetType="Grid"
x:Name="AdditionalAttributeDisplay"
>
<Style.Resources>
<Style TargetType="ItemsControl">
<Setter Property="ItemsSource"
Value="{Binding DeviceAttributes}" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedValue, ElementName=DeviceTypeComboBox}"
Value="1">
<Setter Property="ItemsSource"
Value="{Binding DeviceAttributes}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
But I don't know how to create a TextBlock or TextBox with ItemSource binding.
You can change the ItemTemplate of your ItemsControl.
<Style TargetType="ItemsControl">
<Setter Property="ItemTemplate">
<Setter.Value>
<ItemContainerTemplate>
<Grid>
<TextBox Width="100" Height="20" Text={Binding AttributeName}/>
</Grid>
</ItemContainerTemplate>
</Setter.Value>
</Setter>
</Style>
In above snippet replace PropertyName with DeviceAttributes member you want

How to make text start from the left

I've got an editable ComboBox, and when text is added which is too long, it appears like this:
How can I make the textbox start from the beginning of the string?
TextBox txt = sender as TextBox;
txt.Text = "[Children]";
<Style TargetType="TextBox">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="BorderBrush" Value="Silver"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border Name="Border" Padding="1" Background="#FFFFFF" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="#EEEEEE"/>
<Setter TargetName="Border" Property="BorderBrush" Value="#EEEEEE"/>
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
ComboBox:
ComboBox cmbValue1 = new ComboBox();
cmbValue1.IsTextSearchEnabled = false;
cmbValue1.IsEditable = true;
cmbValue1.Width = 70;
TextBox txtEdit = (TextBox)((sender as ComboBox).Template.FindName("PART_EditableTextBox", (sender as ComboBox)));
txtEdit.Tag = selection;
You need to set the selection start back to 0 in order to make it "left aligned" once the user types something in.
Do this via the TextBoxBase.PreviewLostKeyboardFocus event - here's some XAML:
<ComboBox Name="ComboBox1"
IsEditable="True"
TextBoxBase.PreviewLostKeyboardFocus="TextBox_PreviewLostKeyboardFocus_1">
And the event itself:
private void TextBox_PreviewLostKeyboardFocus_1(object sender, KeyboardFocusChangedEventArgs e)
{
TextBox txtEdit = (TextBox)((sender as ComboBox).Template.FindName("PART_EditableTextBox", (sender as ComboBox)));
txtEdit.SelectionStart = 0;
}
I think this should give enough for you to adapt to your application. I tested in an empty WPF app and it works whether you press TAB or click on another part of the UI with the mouse.
Edit:
Here's how to add the event in code:
Not sure how your app is structured, but again, this works for me:
private void Window_Initialized_1(object sender, EventArgs e)
{
ComboBox cmbValue1 = new ComboBox();
cmbValue1.IsTextSearchEnabled = false;
cmbValue1.IsEditable = true;
cmbValue1.Width = 70;
cmbValue1.PreviewLostKeyboardFocus += TextBox_PreviewLostKeyboardFocus_1;
this.MyCanvas.Children.Add(cmbValue1);
}
(Using same event handler I posted above in this answer)
Edit:
Here is a link to the working project: Note -- it works if the text in editable ComboBox is not selected when you lose focus (i.e., after you type something or just unselect). I'll try and fix that.
http://23.23.250.9/wpfresource.zip

Get style from PhoneResources and apply to new ListBox

I have this style in PhoneApplicationPage.Resources:
<phone:PhoneApplicationPage.Resources>
<data:CarListView x:Key="carCollection" />
<Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem">
<Setter Property="Template">
....
I am trying to add new ListBox with just one item to StackPanel. It just shows the name of class. I tried many ways. For example this:
ListBox lstBox = new ListBox();
CarListView view = new CarListView();
view.DataCollection.Add(new CarView("John", "Ferrari", "/Images/car_missing.jpg"));
lstBox.ItemsSource = view.DataCollection;
lstBox.Style = Application.Current.Resources["ListBoxItemStyle1"] as Style;
stackPanel.Children.Insert(0, lstBox);
Style and classes are alright. When I am not adding this in code but in xaml when page is loaded everything looks fine. How can I add new listbox from code with style from resources?
I have made a sample where i create it all in the code and loads the style from the pages resources as in your sample
The XAML:
<phone:PhoneApplicationPage.Resources>
<Style x:Key="myLBStyle"
TargetType="ListBoxItem">
<Setter Property="Background"
Value="Khaki" />
<Setter Property="Foreground"
Value="DarkSlateGray" />
<Setter Property="Margin"
Value="5" />
<Setter Property="FontStyle"
Value="Italic" />
<Setter Property="FontSize"
Value="14" />
<Setter Property="BorderBrush"
Value="DarkGray" />
</Style>
</phone:PhoneApplicationPage.Resources>
Then i have an empty stackpanel where i add the listbox when the user click on a button
The code behind file:
private void Test_Click_1(object sender, System.Windows.RoutedEventArgs e)
{
ListBox lstBox = new ListBox();
List<string> data = new List<string>() { "one", "two", "three" };
lstBox.ItemsSource = data;
lstBox.ItemContainerStyle = this.Resources["myLBStyle"] as Style;
MyStackPanel.Children.Insert(0, lstBox);
}
You have to use the ItemContainerStyle for the listboxitems, the Style is for the ListBox control!
<Grid x:Name="LayoutRoot" Background="White">
<Grid.Resources>
<Style x:Key="myLBStyle" TargetType="ListBoxItem">
<Setter Property="Background" Value="Khaki" />
<Setter Property="Foreground" Value="DarkSlateGray" />
<Setter Property="Margin" Value="5" />
<Setter Property="FontStyle" Value="Italic" />
<Setter Property="FontSize" Value="14" />
<Setter Property="BorderBrush" Value="DarkGray" />
</Style>
</Grid.Resources>
<ListBox Height="184" ItemContainerStyle="{StaticResource myLBStyle}" HorizontalAlignment="Left"
Margin="23,24,0,0" Name="listBox1" VerticalAlignment="Top" Width="204" >
<ListBox.Items>
<ListBoxItem Content="Item1" />
<ListBoxItem Content="Item2" />
<ListBoxItem Content="Item3" />
</ListBox.Items>
</ListBox>
</Grid>
Or in code:
listBox1.ItemContainerStyle = Application.Current.Resources["myLBStyle"] as Style;

Categories