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
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;
}
}
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
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
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
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;