I'm using GridView with a ListView control to show some catalog content. I'm loading the ListView content dynamically with code behind, creating GridViewColumns and binding them to properties in my custom catListItem class.
var view = new GridView();
var binding = new Binding("Name");
var resElement = _mResourceManager.GetElementByMdlID("vlu_usw_name_of");
view.Columns.Add(new GridViewColumn { Header = resElement.Name, DisplayMemberBinding = binding });
binding = new Binding("Number");
resElement = _mResourceManager.GetElementByMdlID("vlu_usw_arc_logical_nmbr");
view.Columns.Add(new GridViewColumn { Header = resElement.Name, DisplayMemberBinding = binding });
Everything is fine, but now I'm trying to add column with some icon, using the CellTemplate property of the GridViewColumn. Something like this:
var view = new GridView();
var col = new GridViewColumn { Header = "" };
var template = new System.Windows.DataTemplate(typeof(Image));
col.CellTemplate = template;
view.Columns.Add(col);
var binding = new Binding("Name");
var resElement = _mResourceManager.GetElementByMdlID("vlu_usw_name_of");
view.Columns.Add(new GridViewColumn { Header = resElement.Name, DisplayMemberBinding = binding });
binding = new Binding("Number");
resElement = _mResourceManager.GetElementByMdlID("vlu_usw_arc_logical_nmbr");
view.Columns.Add(new GridViewColumn { Header = resElement.Name, DisplayMemberBinding = binding });
I know there's a priority when using DisplayMemberBinding, CellTemplate and CellTemplateSelector.
So my question is:
How can I create (set, ...) content of the CellTemplate and probably bind it to a property of my custom class dynamically? I don't know what do I miss! I've searched for that issue, but everything I found is XAML solutions using DataTemplate.
It's important to do it with code behind.
Thanks in advance!
Actually I found the solution
HERE
I had to use FrameworkElementFactory. Here's the code I was looking for:
var column = new GridViewColumn { Header = "" };
var customTemplate = new System.Windows.DataTemplate();
var efImage = new FrameworkElementFactory(typeof(Image));
efImage.SetBinding(Image.SourceProperty, new Binding("Icon"));
customTemplate.VisualTree = efImage;
column.CellTemplate = customTemplate;
view.Columns.Add(column);
Related
I would like to add a string to my DataBinding with C# code behind and show them in a ListView column. For example I need to add "USD" string to the current_price binding and show in the ListView.
I have tried to solve the issue with string.Format(), but It did not suceed until now. I also tried string.Format("{0:C}", "current_price"), but it does not worked too.
GridView gridView = new GridView();
listView_coins.View = gridView;
gridView.Columns.Add(new GridViewColumn
{
Header = "#",
DisplayMemberBinding = new Binding("market_cap_rank")
});
gridView.Columns.Add(new GridViewColumn
{
Header = "Name",
DisplayMemberBinding = new Binding("name")
});
gridView.Columns.Add(new GridViewColumn
{
Header = "Ticker",
DisplayMemberBinding = new Binding("symbol")
});
gridView.Columns.Add(new GridViewColumn
{
Header = "Change (24h)",
DisplayMemberBinding = new Binding("price_change_percentage_24h")
});
gridView.Columns.Add(new GridViewColumn
{
Header = "Price",
DisplayMemberBinding = new Binding(string.Format("{0} USD", "current_price"))
});
for (int i = 0; i < coinDatas.Count; i++)
{
listView_coins.Items.Add(new CryptocurrencyDataModel
{
market_cap_rank = coinDatas[i].market_cap_rank,
name = coinDatas[i].name,
symbol = coinDatas[i].symbol.ToUpper(),
price_change_percentage_24h = coinDatas[i].price_change_percentage_24h,
current_price = coinDatas[i].current_price,
});
}
I would like to get a result like 17.260 USD in the ListView column.
It is necessary not to set the path to the format string, but to set the value of the format string to the binding
gridView.Columns.Add(new GridViewColumn
{
Header = "Price",
DisplayMemberBinding = new Binding(nameof(CryptocurrencyDataModel.current_price))
{
StringFormat="{0} USD"
}
});
P.S. You can also set the monetary format of the output once in XAML, and adjust the display of the desired currency by changing the culture language of the UI element.
I have a dinamic datagrid with dinamic number of columns.
Add one column code in my cycle:
CValueConverter valueConverter = new CValueConverter()
{
Field = fieldDg
};
Style textStyle = new Style(typeof(TextBlock));
textStyle.Setters.Add(new Setter(TextBlock.TextTrimmingProperty, TextTrimming.CharacterEllipsis));
textStyle.Setters.Add(
new Setter(
ToolTipService.ToolTipProperty,
new Binding
{
Path = new PropertyPath("[" + cmnIndex.ToString() + "]"),
Converter = valueConverter
}));
this.FormListDg.Columns.Add(new DataGridTextColumn()
{
Header = fieldDg.Name,
HeaderStyle = this.GetHeaderStyle(fieldDg.Color),
CellStyle = this.GetCellStyle(fieldDg.Color),
CanUserSort = true,
MaxWidth = 300,
Binding = new Binding
{
Path = new PropertyPath("[" + cmnIndex.ToString() + "]"),
Converter = valueConverter
},
ElementStyle = textStyle
});
cmnIndex++;
The result of this datagrid cell tooltip is:
When I change the style setter value to constant, everything is works fine:
textStyle.Setters.Add(
new Setter(
ToolTipService.ToolTipProperty,
"VALAMI"));
How can I use binding in style setter value?
The Value of the Setter should to be set to a (data-bound) Tooltip and not Binding:
ToolTip tt = new ToolTip();
tt.SetBinding(ContentControl.ContentProperty, new Binding
{
Path = new PropertyPath("[" + cmnIndex.ToString() + "]"),
Converter = valueConverter
});
textStyle.Setters.Add(
new Setter(
ToolTipService.ToolTipProperty,
tt));
I'm writing a WPF application which makes use of the DataGrid control. I'm using the MaterialDesign theme to style the application and this gives a nice look and feel.
However for complex reasons I wont go into here I'm required to add the columns into the dataGrid programmatically. For some of the columns I'm also styling the columns to highlight pass / fail in red. When I do this I loose 'some of the styling' provided by material design for that columns. Namely the Horizontal and Vertical alignment.
The code to the above is as follows:
// Define Setter
Setter setterResultFail = new Setter();
setterResultFail.Property = DataGridCell.BackgroundProperty;
setterResultFail.Value = Brushes.Red;
// Create a column for the Site.
var currentColumn = new DataGridTextColumn();
currentColumn.Header = "Device #";
currentColumn.Binding = new Binding("Device");
ResultsDataGrid.Columns.Add(currentColumn);
// Create a column for the Site.
currentColumn = new DataGridTextColumn();
currentColumn.Header = "Site";
currentColumn.Binding = new Binding("Site");
ResultsDataGrid.Columns.Add(currentColumn);
// Create a column for the Pass Fail.
currentColumn = new DataGridTextColumn();
currentColumn.Header = "Pass Fail";
currentColumn.Binding = new Binding("PassFail") { Converter = new BooleanToPassFailConverter() };
// Create cellstyle to make the cell 'red' when the PassFail value is False. ( this is done via a data trigger )
cellStyle = new Style(typeof(DataGridCell));
// Define First DataTrigger that sets a CELL red if the value is a fail.
dataTrigger = new DataTrigger();
dataTrigger.Value = "False";
dataTrigger.Binding = new Binding("PassFail");
dataTrigger.Setters.Add(setterResultFail);
// Add the data-triggers to the cell style.
cellStyle.Triggers.Clear();
cellStyle.Triggers.Add(dataTrigger);
// Apply the newly created cell style.
currentColumn.CellStyle = cellStyle;
ResultsDataGrid.Columns.Add(currentColumn);
Clearly the new cellStyle is used instead of the MaterialDesign style. I've tried setting the values for vertical / horizontal manually but I can't get it to look correct:
Setter setterTextContentHorizonalAlignment = new Setter();
setterTextContentHorizonalAlignment.Property = DataGridCell.HorizontalContentAlignmentProperty;
setterTextContentHorizonalAlignment.Value = HorizontalAlignment.Center;
Setter setterTextContentVerticalAlignment = new Setter();
setterTextContentVerticalAlignment.Property = DataGridCell.VerticalContentAlignmentProperty;
setterTextContentVerticalAlignment.Value = VerticalAlignment.Center;
Setter setterTextHorizontalAlignment = new Setter();
setterTextHorizontalAlignment.Property = DataGridCell.HorizontalAlignmentProperty;
setterTextHorizontalAlignment.Value = HorizontalAlignment.Center;
Setter setterTextVerticalAlignment = new Setter();
setterTextVerticalAlignment.Property = DataGridCell.VerticalAlignmentProperty;
setterTextVerticalAlignment.Value = VerticalAlignment.Center;
cellStyle.Setters.Add(setterTextContentHorizonalAlignment);
cellStyle.Setters.Add(setterTextContentVerticalAlignment);
cellStyle.Setters.Add(setterTextHorizontalAlignment);
cellStyle.Setters.Add(setterTextVerticalAlignment);
Is there a way I can add to the style rather than replace it...similar to the BasedOn approch in XAML?
After much wasting of time on this question I came across Danny Beckett's similar question and King King's answer. By using his answer and applying it to the specific cell I was having trouble with it fixed the issue:King King's answer
// Create a column for the Pass Fail.
currentColumn = new DataGridTextColumn();
currentColumn.Header = "Pass Fail";
currentColumn.Binding = new Binding("PassFail") { Converter = new BooleanToPassFailConverter() };
// Create cellstyle to make the cell 'red' when the PassFail value is False. ( this is done via a data trigger )
cellStyle = new Style(typeof(DataGridCell));
// Define First DataTrigger that sets a CELL red if the value is a fail.
dataTrigger = new DataTrigger();
dataTrigger.Value = "False";
dataTrigger.Binding = new Binding("PassFail");
dataTrigger.Setters.Add(setterResultFail);
// Add the data-triggers to the cell style.
cellStyle.Triggers.Clear();
cellStyle.Triggers.Add(dataTrigger);
//root visual of the ControlTemplate for DataGridCell is a Border
var border = new FrameworkElementFactory(typeof(Border));
border.SetBinding(Border.BorderBrushProperty, new Binding("BorderBrush")
{
RelativeSource = RelativeSource.TemplatedParent
});
border.SetBinding(Border.BackgroundProperty, new Binding("Background") { RelativeSource = RelativeSource.TemplatedParent });
border.SetBinding(Border.BorderThicknessProperty, new Binding("BorderThickness") { RelativeSource = RelativeSource.TemplatedParent });
border.SetValue(SnapsToDevicePixelsProperty, true);
//the only child visual of the border is the ContentPresenter
var contentPresenter = new FrameworkElementFactory(typeof(ContentPresenter));
contentPresenter.SetBinding(SnapsToDevicePixelsProperty, new Binding("SnapsToDevicePixelsProperty") { RelativeSource = RelativeSource.TemplatedParent });
contentPresenter.SetBinding(VerticalAlignmentProperty, new Binding("VerticalContentAlignment") { RelativeSource = RelativeSource.TemplatedParent });
contentPresenter.SetBinding(HorizontalAlignmentProperty, new Binding("HorizontalContentAlignment") { RelativeSource = RelativeSource.TemplatedParent });
//add the child visual to the root visual
border.AppendChild(contentPresenter);
//here is the instance of ControlTemplate for DataGridCell
var template = new ControlTemplate(typeof(DataGridCell));
template.VisualTree = border;
//define the style
cellStyle.Setters.Add(new Setter(TemplateProperty, template));
cellStyle.Setters.Add(new Setter(VerticalContentAlignmentProperty, VerticalAlignment.Center));
cellStyle.Setters.Add(new Setter(HorizontalContentAlignmentProperty, HorizontalAlignment.Center));
// Apply the newly created cell style.
currentColumn.CellStyle = cellStyle;
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.
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;