Create DataGridTemplateColumn Through C# Code - c#

I have a dynamic Datagrid that I have created. I am creating each column for it through code behind. I am having troubles on a column that I want to be displayed at a textblock when not editing, but as a combobox while editing. I have an ObservableCollection of Transactions. Each Transaction has a type called "Account". Here is what I have so far:
private DataGridTemplateColumn GetAccountColumn()
{
// Create The Column
DataGridTemplateColumn accountColumn = new DataGridTemplateColumn();
accountColumn.Header = "Account";
Binding bind = new Binding("Account");
bind.Mode = BindingMode.TwoWay;
// Create the TextBlock
FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock));
textFactory.SetBinding(TextBlock.TextProperty, bind);
DataTemplate textTemplate = new DataTemplate();
textTemplate.VisualTree = textFactory;
// Create the ComboBox
bind.Mode = BindingMode.OneWay;
FrameworkElementFactory comboFactory = new FrameworkElementFactory(typeof(ComboBox));
comboFactory.SetValue(ComboBox.DataContextProperty, this.Transactions);
comboFactory.SetValue(ComboBox.IsTextSearchEnabledProperty, true);
comboFactory.SetBinding(ComboBox.ItemsSourceProperty, bind);
DataTemplate comboTemplate = new DataTemplate();
comboTemplate.VisualTree = comboFactory;
// Set the Templates to the Column
accountColumn.CellTemplate = textTemplate;
accountColumn.CellEditingTemplate = comboTemplate;
return accountColumn;
}
The value displays in the TextBlock. However, in the combobox, I am only getting one character to display per item. For example, here is the textblock:
But when I click to edit and go into the combobox, here is what is shown:
Can someone help me out so that the items in the Combobox are displayed properly? Also, when I select something from the Combobox, the textblock isn't updated with the item I selected.
UPDATED:
Here is my column as of now. The items in the ComboBox are being displayed properly. The issue now is that when a new item is selected, the text in the TextBlock isn't updated with the new item.
private DataGridTemplateColumn GetAccountColumn()
{
// Create The Column
DataGridTemplateColumn accountColumn = new DataGridTemplateColumn();
accountColumn.Header = "Account";
Binding bind = new Binding("Account");
bind.Mode = BindingMode.OneWay;
// Create the TextBlock
FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock));
textFactory.SetBinding(TextBlock.TextProperty, bind);
DataTemplate textTemplate = new DataTemplate();
textTemplate.VisualTree = textFactory;
// Create the ComboBox
Binding comboBind = new Binding("Account");
comboBind.Mode = BindingMode.OneWay;
FrameworkElementFactory comboFactory = new FrameworkElementFactory(typeof(ComboBox));
comboFactory.SetValue(ComboBox.IsTextSearchEnabledProperty, true);
comboFactory.SetValue(ComboBox.ItemsSourceProperty, this.Accounts);
comboFactory.SetBinding(ComboBox.SelectedItemProperty, comboBind);
DataTemplate comboTemplate = new DataTemplate();
comboTemplate.VisualTree = comboFactory;
// Set the Templates to the Column
accountColumn.CellTemplate = textTemplate;
accountColumn.CellEditingTemplate = comboTemplate;
return accountColumn;
}
The "Accounts" property is declared like this in my MainWindow class:
public ObservableCollection<string> Accounts { get; set; }
public MainWindow()
{
this.Types = new ObservableCollection<string>();
this.Parents = new ObservableCollection<string>();
this.Transactions = new ObservableCollection<Transaction>();
this.Accounts = new ObservableCollection<string>();
OpenDatabase();
InitializeComponent();
}
Here is my Transaction Class:
public class Transaction
{
private string date;
private string number;
private string account;
public string Date
{
get { return date; }
set { date = value; }
}
public string Number
{
get { return number; }
set { number = value; }
}
public string Account
{
get { return account; }
set { account = value; }
}
}

You bind the ItemsSource to the selected value, a string, aka char array, so every character is used as an item, the ItemsSource binding presumably should target some other collection from which the value can be chosen.

Dim newBind As Binding = New Binding("LinktoCommonOutputBus")
newBind.Mode = BindingMode.OneWay
factory1.SetValue(ComboBox.ItemsSourceProperty, dictionary)
factory1.SetValue(ComboBox.NameProperty, name)
factory1.SetValue(ComboBox.SelectedValuePathProperty, "Key")
factory1.SetValue(ComboBox.DisplayMemberPathProperty, "Value")
factory1.SetBinding(ComboBox.SelectedValueProperty, newBind)
By creating Binding you can set SelectedValue in a datagrid for WPF.

Related

How to get value of a programmatically written combobox in a datagrid in wpf?

To follow my previous post here => Binding SelectedItem of ComboBox in DataGrid with different type
I have now a datagrid containing 2 columns, one with a text, the other with a combobox (in a datatemplate, written thru the C# code, not the Xaml).
After having done some choice on the combobox, I now would like to parse the result but the value of the cell containing my combobox stay empty :
foreach(DataRowView row in Datagrid1.Items)
{
var firstColumNresult = row.Row.ItemArray[0];// Return correctly a string
var myrow = row.Row.ItemArray[1];// always empty...
}
The result is that I cant get the values of my (previously generated) combobox.
I suppose one binding must missed somewhere...
This is the combobox creation code :
DataTable tableForDG = new DataTable();
tableForDG.Columns.Add(new DataColumn { ColumnName = "Name", Caption = "Name" });
tableForDG.Columns.Add(new DataColumn { ColumnName = "Attachment", Caption = "Attachment" }); // this column will be replaced
tableForDG.Columns.Add(new DataColumn { ColumnName = "AttachmentValue", Caption = "AttachmentValue" });
tableForDG.Columns.Add(new DataColumn { ColumnName = "DisplayCombo", Caption = "DisplayCombo", DataType=bool });
// Populate dataview
DataView myDataview = new DataView(tableForDG);
foreach (var value in listResults)// a list of string
{
DataRowView drv = myDataview.AddNew();
drv["Name"] = value.Name;
drv["Attachment"] = value.Name;// this column will be replaced...
drv["DisplayCombo"] = true;// but it can be false on my code...
}
var DG = myDataview;//
Datagrid1.ItemsSource = DG;
Datagrid1.AutoGenerateColumns = true;
Datagrid1.Items.Refresh();
DataGridTemplateColumn dgTemplateColumn = new DataGridTemplateColumn();
dgTemplateColumn.Header = "Attachment";
var newCombobox = new FrameworkElementFactory(typeof(ComboBox));
newCombobox.SetValue(ComboBox.NameProperty, "myCBB");
Binding enableBinding = new Binding();
newCombobox.SetValue(ComboBox.IsEnabledProperty, new Binding("DisplayCombo"));
newCombobox.SetValue(ComboBox.SelectedValueProperty, new Binding("AttachmentValue"));
List<string> listUnitAlreadyAttached = new List<string>();
// fill the list...
enableBinding.Source = listUnitAlreadyAttached;
newCombobox.SetBinding(ComboBox.ItemsSourceProperty, enableBinding);
var dataTplT = new DataTemplate();
dataTplT.VisualTree = newCombobox;
dgTemplateColumn.CellTemplate = dataTplT;
Datagrid1.Columns[1] = dgTemplateColumn;
Any idea/advice ?
You should explicitely specify the binding mode and update trigger of your binding. Also use SetBinding instead of SetValue:
var valueBinding = new Binding("AttachmentValue")
{
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
newCombobox.SetBinding(ComboBox.SelectedValueProperty, valueBinding);
This should enable you to get the selected value into your row data. It might not update in the displayed datagrid value for the AttachmentValue column.

binding a property to the colomn in RadGridView in code behind

I have a list of custom classes that I have bound them to the RadGridView through the below code:
this.ItemsSource = CorrelationCalibraationGridInput.ListOfCalibratableCorrelationClasses;
then I have created the columns manually. For one of the columns that is check box column, I need to enable disable the check box binding to a property of class and set its check state based on another property of the class.
I used the code below but the enablity does not bind to the IsNotCalibratedYet property. Can you explain why and how can I solve it?(note that the check state is correctly binded to the IsCalibratedUSed property of the class).
GridViewDataColumn IsCalibratedUSedColumn = new GridViewDataColumn()
{
UniqueName = "IsCalibratedUSedColumn",
Header = "Use calibrated",
DataMemberBinding = new Binding("IsCalibratedUSed"),
IsFilterable = false,
};
Binding enablityBinding = new Binding("IsNotCalibratedYet");
enablityBinding.Mode= BindingMode.OneWay;
enablityBinding.UpdateSourceTrigger= UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(IsCalibratedUSedColumn, GridViewDataColumn.IsEnabledProperty,enablityBinding );
this.Columns.Add(IsCalibratedUSedColumn);
You should set the IsReadOnlyBinding property of the GridViewDataColumn to your Binding:
GridViewDataColumn IsCalibratedUSedColumn = new GridViewDataColumn()
{
UniqueName = "IsCalibratedUSedColumn",
Header = "Use calibrated",
DataMemberBinding = new Binding("IsCalibratedUSed"),
IsFilterable = false,
};
Binding enablityBinding = new Binding("IsNotCalibratedYet");
enablityBinding.Mode = BindingMode.OneWay;
enablityBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
IsCalibratedUSedColumn.IsReadOnlyBinding = enablityBinding;
this.Columns.Add(IsCalibratedUSedColumn);
Depending on whether your source property returns true/false you may want to use an InvertedBooleanConverter:
Binding enablityBinding = new Binding("IsNotCalibratedYet");
enablityBinding.Mode = BindingMode.OneWay;
enablityBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
enablityBinding.Converter = new InvertedBooleanConverter();
You can do this using the following setting:
public MainWindow()
{
InitializeComponent();
lv.ItemsSource = new Item[3] { new Item() { IsNotCalibratedYet=true }, new Item() { IsNotCalibratedYet = false }, new Item() { IsNotCalibratedYet = true } };
gv.Columns.Add(new GridViewColumn()
{
DisplayMemberBinding = new Binding("IsNotCalibratedYet"),
});
}
in which:
public class Item
{
public bool IsNotCalibratedYet { get; set; }
}
and Xaml is
<ListView Name="lv" >
<ListView.View>
<GridView x:Name="gv" AllowsColumnReorder="true"
ColumnHeaderToolTip="Employee Information">
</GridView>
</ListView.View>
</ListView>

C# Displaying text box (present in the list view) value in a message box (wpf)

I have a list view in which each row contains 5 entries. The list box looks like this:
I need to store (display) the name of variable and the value present in "Physical value" column text box when i press OK button. For e.g. if i enter 45 in the physical value text box (only one row at a time) then the name of the variable and value "45" should be stored (displayed). I am able to retrieve the name of the variables but not the value of the text box.
My try:
This code will populate the list view with variables and bind it to the properties.
public void Populatevariables(IList<string> variables)
{
int rowcount = 0;
dataGrid.RowDefinitions.Clear();
dataGrid.ColumnDefinitions.Clear();
RowDefinition rd = new RowDefinition();
rd.Height = new GridLength();
dataGrid.RowDefinitions.Add(rd);
dataGrid.RowDefinitions.Add(new RowDefinition());
dataGrid.ColumnDefinitions.Add(new ColumnDefinition());
Label t1 = new Label();
t1.Content = "Variables";
Grid.SetColumn(t1, 0);
Grid.SetRow(t1, rowcount);
dataGrid.Children.Add(t1);
ListView VrblPopulateList = new ListView();
GridView g1 = new GridView();
g1.AllowsColumnReorder = true;
//l1.View = g1;
GridViewColumn g2 = new GridViewColumn();
g2.Header = "Name";
g2.Width = 200;
g2.DisplayMemberBinding = new Binding("Name");
g1.Columns.Add(g2);
GridViewColumn g5 = new GridViewColumn();
g5.Header = "DataType";
g5.Width = 200;
g5.DisplayMemberBinding = new Binding("DataType");
g1.Columns.Add(g5);
GridViewColumn g3 = new GridViewColumn();
g3.Header = "Current Value";
g3.Width = 200;
DataTemplate dt1 = new DataTemplate();
FrameworkElementFactory FF1 = new FrameworkElementFactory(typeof(TextBox));
FF1.SetBinding(TextBox.BindingGroupProperty, new Binding("Current_Value"));
FF1.SetValue(FrameworkElement.HeightProperty, Height = 30);
FF1.SetValue(FrameworkElement.WidthProperty, Width = 150);
dt1.VisualTree = FF1;
g3.CellTemplate = dt1;
g1.Columns.Add(g3);
GridViewColumn g6 = new GridViewColumn();
g6.Header = "Physical Value";
g6.Width = 200;
DataTemplate dt2 = new DataTemplate();
FrameworkElementFactory FF2 = new FrameworkElementFactory(typeof(TextBox));
FF2.SetBinding(TextBox.BindingGroupProperty, new Binding("Physical_Value"));
//FF2.AddHandler(TextBox.TextChangedEvent, txtchanged, true);
FF2.SetValue(FrameworkElement.HeightProperty, Height = 30);
FF2.SetValue(FrameworkElement.WidthProperty, Width = 150);
dt2.VisualTree = FF2;
g6.CellTemplate = dt2;
g1.Columns.Add(g6);
GridViewColumn g4 = new GridViewColumn();
g4.Header = "Action";
g4.Width = 200;
DataTemplate dt = new DataTemplate();
FrameworkElementFactory FF = new FrameworkElementFactory(typeof(Button));
FF.SetBinding(Button.BindingGroupProperty, new Binding("ToDo"));
FF.SetValue(FrameworkElement.HeightProperty,Height = 30);
FF.SetValue(FrameworkElement.WidthProperty, Width = 150);
FF.SetValue(System.Windows.Controls.Button.ContentProperty,"OK");
FF.AddHandler(Button.ClickEvent, new RoutedEventHandler(b1_click));
dt.VisualTree = FF;
g4.CellTemplate = dt;
g1.Columns.Add(g4);
VrblPopulateList.View = g1;
Grid.SetRow(VrblPopulateList, rowcount + 1);
dataGrid.Children.Add(VrblPopulateList);
for (int i = 0; i < variables.Count; i++)
{
Label lb1 = new Label();
lb1.Content = variables[i].Name;
Label lb2 = new Label();
lb2.Name = variables[i].datatype;
DataTemplate dd = new DataTemplate();
TextBox tb = new TextBox();
tb.Name = "TextBox" + i.ToString();
Button b1 = new Button();
VrblPopulateList.Items.Add(new User() { Name = lb1.Content, DataType = lb2.Name, Current_Value = tb, Physical_Value = tb, ToDo = b1 });
}
}
This code defines the property which is bind while populating:
public class User
{
public object Name { get; set; }
public string DataType { get; set; }
public Control Current_Value
{
get;
set;
}
public Control Physical_Value
{
get;
set;
}
public Control ToDo { get; set; }
}
At last this code will retrieve all the items when button is clicked.
private void b1_click(object sender, RoutedEventArgs e)
{
User item = (User)((Button)sender).DataContext;
TextBox t = (TextBox)item.Physical_Value;
MessageBox.Show(item.Name.ToString() + t.Text);
}
The text box value is always empty. I know it can be solved by adding handler to "text changed" event while populating. But i dont know how to do it. Please help.
As mentioned in the comment by #Ponas Justas i have to do following changes in my code:
Set the UpdateSourceTrigger property of the text box.
Change the user model accordingly.
After doing above changes my code looks like:
FF2.SetBinding(TextBox.BindingGroupProperty, new Binding("Physical_Value"));
FF2.SetBinding(TextBox.TextProperty, new Binding("PhysicalValueTxtChanged") { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });
User Model
public class User
{
public object Name { get; set; }
public string DataType { get; set; }
public Control Current_Value
{
get;
set;
}
public string _PhysicalValueTxtChanged = null;
public string PhysicalValueTxtChanged
{
get { return _PhysicalValueTxtChanged; }
set
{
_PhysicalValueTxtChanged = value;
}
}
public Control ToDo { get; set; }
}
After doing that the text in the text box can be easily stored by just modifying like this:
private void b1_click(object sender, RoutedEventArgs e)
{
User item = (User)((Button)sender).DataContext;
string txt = item.PhysicalValueTxtChanged;
MessageBox.Show(item.Name.ToString() + txt);
}
Thanks a lot Ponas Justas.

WPF DataGridComboBoxColumn does not display text if it is not in ItemsSource

In my C# WPF application I programmatically add a ComboBoxColumn to a DataGrid:
public static DataGridComboBoxColumn getCboCol(string colName, Binding textBinding)
{
List<string> statusItemsList = new StatusStrList();
DataGridComboBoxColumn cboColumn = new DataGridComboBoxColumn();
cboColumn.Header = colName;
cboColumn.SelectedItemBinding = textBinding;
cboColumn.ItemsSource = statusItemsList;
return cboColumn;
}
If an item in the containing DataGrid contains text, which my StatusStrList doesn't contain, it won't be displayed.
Example: If my StatusStrList contains A, B, C and a DataGrid's item has X, the X won't be displayed as text in the ComboBox.
How can I fix this?
Thanks in advance,
Christian
DataGridComboBoxColumn isn't dynamic enough to do something like this but you can use DataGridTemplateColumn. Code below should achieve the functionality you need. It works by using a CellTemplate containing a TextBlock which easily displays an item that wouldn't be in the ItemsSource of the ComboBox. Going into edit mode will bring up the ComboBox that contains all of the items of the list.
DataGridTemplateColumn cboColumn = new DataGridTemplateColumn();
cboColumn.Header = colName;
//DataTemplate for CellTemplate
DataTemplate cellTemplate = new DataTemplate();
FrameworkElementFactory txtBlkFactory = new FrameworkElementFactory(typeof(TextBlock));
txtBlkFactory.SetValue(TextBlock.TextProperty, textBinding);
cellTemplate.VisualTree = txtBlkFactory;
cboColumn.CellTemplate = cellTemplate;
//DataTemplate for CellEditingTemplate
DataTemplate editTemplate = new DataTemplate();
FrameworkElementFactory cboFactory = new FrameworkElementFactory(typeof(ComboBox));
cboFactory.SetValue(ComboBox.TextProperty, textBinding);
cboFactory.SetValue(ComboBox.ItemsSourceProperty, statusItemsList);
cboFactory.SetValue(ComboBox.IsEditableProperty, true);
MouseEventHandler handler = new MouseEventHandler(delegate(object sender, MouseEventArgs args)
{
ComboBox cboBox = (ComboBox)sender;
cboBox.IsDropDownOpen = true;
});
cboFactory.AddHandler(ComboBox.MouseEnterEvent, handler);
editTemplate.VisualTree = cboFactory;
cboColumn.CellEditingTemplate = editTemplate;

PropertyPath with parenthesis with the binding

I am creating the binding in the code behind.
IEnumerable<IDictionary<string, object>> rows = dg1.ItemsSource.OfType<IDictionary<string, object>>();
IEnumerable<string> columns = rows.SelectMany(d => d.Keys). Distinct(StringComparer.OrdinalIgnoreCase);
foreach (string column in columns)
{
DataGridTemplateColumn col = new DataGridTemplateColumn();
col.Header = column;
// Create a factory. This will create the controls in each cell of this
// column as needed.
FrameworkElementFactory factory =
new FrameworkElementFactory(typeof(TextBox));
**Binding b = new Binding() { Path=new PropertyPath(column)};**
b.Mode = BindingMode.TwoWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus;
// b.StringFormat = "d";
b.Converter = new MyConverter();
b.ConverterParameter = dg1;
factory.SetValue(TextBox.TextProperty, b);
// factory.AddHandler(TextBox.LostFocusEvent, new RoutedEventHandler(TextBox_LostFocus));
// Create the template itself, and add the factory to it.
DataTemplate cellEditingTemplate = new DataTemplate();
cellEditingTemplate.VisualTree = factory;
col.CellTemplate = cellEditingTemplate;
dg1.Columns.Add(col);
Now, if the value of the column starts with an opening ( and if it does not contain the closing braces ) then it gives the following error..
Syntax error in PropertyPath 'Unmatched parenthesis'
You can simply reproduce it like this:
public Window3()
{
try
{
InitializeComponent();
Binding b = new Binding() { Path = new PropertyPath("a(") };
}
catch (Exception)
{
throw;
}
}
Moreover if the PropertyPath has the value like (abc) then also the binding fails .
What could be the solution to avoid such binding issues?
EDIT:
Any hint from this answer.

Categories

Resources