How do I recreate the following XAML databinding in code? I have most of it except for the DataTemplate definition.
Here is an example of the DataBinding in XAML
<GridViewColumn Width="140" Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Label}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Here is the code I have so far:
return new GridViewColumn()
{
Header = header,
Width = width,
DisplayMemberBinding = new System.Windows.Data.Binding(bindingProperty)
};
The problem is, how did I set the CellTemplate for the DataTemplate through code?
For anyone interested, here is the solution:
private GridViewColumn GetGridViewColumn(string header, double width, string bindingProperty, Type type)
{
GridViewColumn column = new GridViewColumn();
column.Header = header;
FrameworkElementFactory controlFactory = new FrameworkElementFactory(type);
var itemsBinding = new System.Windows.Data.Binding(bindingProperty);
controlFactory.SetBinding(TextBox.TextProperty, itemsBinding);
DataTemplate template = new DataTemplate();
template.VisualTree = controlFactory;
column.CellTemplate = template;
return column;
}
Related
I'm experimenting with code-behind created WPF masks as prototype for a WPF mask designer.
In my ViewModel i have a DataTable and a DataView (which is simply the DefaultView of the DataTable).
In my DataTable i got two columns ("vorname" and "nachname") and four rows.
In my WPF mask i want to have a DataGrid and two TextBoxes, which are binded to the SelectedItem of the DataGrid and the columns (either "vorname" or "nachname").
When i select an item in the DatGrid at runtime, the data from that item shall be showed in the TextBoxes.
First i tried to define the DataGrid in the XAML file and generate the TextBoxes an their bindings in code.
Here it works fine.
I select an item in the DataGrid and the data of the item is showed in the TextBoxes.
But when i generate the grid in code, it doesn't work anymore.
Is there some sort of NotifyOnSelectedIndexChanged, that i'm missing?
Any help will be appreciated.
This is the XAML:
<Window x:Class="DesignerTest.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow"
Height="400"
Width="600">
<DockPanel x:Name="mainpanel">
<!--<DataGrid x:Name="datagrid"
DockPanel.Dock="Top"
Height="120" />-->
<WrapPanel x:Name="wrappanel">
<!--<TextBox x:Name="vornameSelected" Width="150" Margin="5" Text="{Binding SelectedItem.vorname, ElementName=datagrid}" IsEnabled="False" />
<TextBox x:Name="nachnameSelected" Width="150" Margin="5" Text="{Binding SelectedItem.nachname, ElementName=datagrid}" IsEnabled="False" />-->
<!--<TextBox x:Name="vornameSelected" Width="150" Margin="5" IsEnabled="False" />
<TextBox x:Name="nachnameSelected" Width="150" Margin="5" IsEnabled="False" />-->
</WrapPanel>
</DockPanel>
</Window>
And this is the code for creating and binding:
// The ViewModel und the DataTable are created.
_vm = new SerializingTestViewModel();
_vm.CreateDataTable();
this.DataContext = _vm.DataTable;
// The DataGrid and it's Binding are created.
DataGrid datagrid = new DataGrid();
datagrid.Name = "datagrid";
DockPanel.SetDock(datagrid, Dock.Top);
datagrid.Height = 120;
datagrid.ItemsSource = _vm.DataSource;
mainpanel.Children.Add(datagrid);
// The Textboxes and the Bindings are created.
TextBox vornameSelected = new TextBox();
vornameSelected.Name = "vornameSelected";
vornameSelected.Width = 150;
Thickness margin = new Thickness(5);
vornameSelected.SetValue(TextBox.MarginProperty, margin);
vornameSelected.IsEnabled = false;
Binding selectedItemBinding = new Binding();
selectedItemBinding.ElementName = "datagrid";
selectedItemBinding.Path = new PropertyPath("SelectedItem.vorname");
vornameSelected.SetBinding(TextBox.TextProperty, selectedItemBinding);
wrappanel.Children.Add(vornameSelected);
TextBox nachnameSelected = new TextBox();
nachnameSelected.Name = "nachnameSelected";
nachnameSelected.Width = 150;
margin = new Thickness(5);
nachnameSelected.SetValue(TextBox.MarginProperty, margin);
nachnameSelected.IsEnabled = false;
selectedItemBinding = new Binding();
selectedItemBinding.ElementName = "datagrid";
selectedItemBinding.Path = new PropertyPath("SelectedItem.nachname");
nachnameSelected.SetBinding(TextBox.TextProperty, selectedItemBinding);
wrappanel.Children.Add(nachnameSelected);
Try setting your binding source using the Source property instead of ElementName
//selectedItemBinding.ElementName = "datagrid"
selectedItemBinding.Source = datagrid;
The problem might be that the ElementName lookup for items is not working as expected because items are added dynamically at runtime via code behind.
I want to add a button on every row of WPF grid which I am binding from code behind. I am very new to WPF any help is appreciated.
My current code for binding grid is:
DataGridTextColumn c1 = new DataGridTextColumn();
c1.Header = "Dummy column";
c1.Binding = new Binding("DummyColumn");
c1.IsReadOnly = true;
grdDummy.Columns.Add(c1);
foreach (DummyObject deal in AllDummyObjects)
{
ModelToBind dataModel = new ModelToBind()
//do some processing on dataModel
grdDummy.Items.Add(dataModel);
}
You can add another column with button like this:
DataGridTemplateColumn buttonColumn = new DataGridTemplateColumn();
DataTemplate buttonTemplate = new DataTemplate();
FrameworkElementFactory buttonFactory = new FrameworkElementFactory(typeof (Button));
buttonTemplate.VisualTree = buttonFactory;
//add handler or you can add binding to command if you want to handle click
buttonFactory.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(HandleClick));
buttonFactory.SetValue(ContentProperty, "Button");
buttonColumn.CellTemplate = buttonTemplate;
grdDummy.Columns.Add(buttonColumn);
Previously, an example was given of creating a Button using FrameworkElementFactory.
This class is not recommended.
Quote from the documentation:
This class is a deprecated way to programmatically create templates, which are subclasses of FrameworkTemplate such as ControlTemplate or DataTemplate; not all of the template functionality is available when you create a template using this class. The recommended way to programmatically create a template is to load XAML from a string or a memory stream using the Load method of the XamlReader class.
In this regard, I show the implementation code using the XamlReader.
The code is shown from the assumption that the ModelToBind class has a command-property named ButtonCommand and property ButtonTitle.
And this class is located in the local namespace "Febr20y" in the assembly of the same name.
DataGridTextColumn c1 = new DataGridTextColumn
{
Header = "Dummy column",
Binding = new Binding("DummyColumn"),
IsReadOnly = true
};
DataTemplate template = (DataTemplate)XamlReader.Parse(
#"<DataTemplate
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Febr20y;assembly=Febr20y'
DataType ='{x:Type local:ModelToBind}'>
<Button Content='{Binding ButtonTitle, Mode=OneWay}'
Command='{Binding ButtonCommand, Mode=OneWay}'/>
</DataTemplate>");
DataGridTemplateColumn c2 = new DataGridTemplateColumn()
{
Header = "Buttons",
IsReadOnly = true,
CellTemplate=template
};
grdDummy.Columns.Add(c1);
grdDummy.Columns.Add(c2);
var listSource = AllDummyObjects
.Select(deal => new ModelToBind() { ButtonTitle = deal.Title.ToString()})
.ToList();
grdDummy.ItemsSource = listSource;
This is equivalent to this XAML code:
<DataGrid x:Name="grdDummy" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding DummyColumn}"
IsReadOnly="True"
Header="Dummy column"/>
<DataGridTemplateColumn Header="Buttons"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type local:ModelToBind}">
<Button Content="{Binding ButtonTitle, Mode=OneWay}"
Command="{Binding ButtonCommand, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I've already search for any solutions but I just couldn't find one. I've found several post in several websites/forum that discusses my problem, but I want a very simple solution.
while (SQLDataReader.Read())
{
var view = new GridView();
view.Columns.Add(new GridViewColumn { Header = "User", DisplayMemberBinding = new Binding("User") });
view.Columns.Add(new GridViewColumn { Header = "hostNamedb", DisplayMemberBinding = new Binding("hostNamedb") });
view.Columns.Add(new GridViewColumn { Header = "fullName", DisplayMemberBinding = new Binding("fullName") });
view.Columns.Add(new GridViewColumn { Header = "location", DisplayMemberBinding = new Binding("location") });
view.Columns.Add(new GridViewColumn { Header = "department", DisplayMemberBinding = new Binding("department") });
view.Columns.Add(new GridViewColumn { Header = "position", DisplayMemberBinding = new Binding("postion") });
view.Columns.Add(new GridViewColumn { Header = "message", DisplayMemberBinding = new Binding("message") });
view.Columns.Add(new GridViewColumn { Header = "status", DisplayMemberBinding = new Binding("status") });
view.Columns.Add(new GridViewColumn { Header = "ip", DisplayMemberBinding = new Binding("ip") });
ListView2.View = view;
ListView2.Items.Add(new
{
User = SQLDataReader["User"].ToString(),
hostNamedb = SQLDataReader["hostNamedb"].ToString(),
fullName = SQLDataReader["fullName"].ToString(),
location = SQLDataReader["location"].ToString(),
department = SQLDataReader["department"].ToString(),
position = SQLDataReader["position"].ToString(),
message = SQLDataReader["message"].ToString(),
status = SQLDataReader["status"].ToString(),
ip = SQLDataReader["ip"].ToString(),
});
}
userName_click = System.Convert.ToString(ListView2.Items[var2].User.Text.ToString());
hostName_click = System.Convert.ToString(ListView2.Items[var2].hostNamedb.Text.ToString());
fullName_form = System.Convert.ToString(ListView2.Items[var2].SubItems[2].Text.ToString());
location_form = System.Convert.ToString(ListView2.Items[var2].SubItems[3].Text.ToString());
department_form = System.Convert.ToString(ListView2.Items[var2].SubItems[4].Text.ToString());
position_form = System.Convert.ToString(ListView2.Items[var2].SubItems[5].Text.ToString());
message_Form = System.Convert.ToString(ListView2.Items[var2].SubItems[6].Text.ToString());
status_form = System.Convert.ToString(ListView2.Items[var2].SubItems[7].Text.ToString());
id_form = System.Convert.ToString(ListView2.Items[var2].SubItems[8].Text.ToString());
userIP_click = System.Convert.ToString(ListView2.Items[var2].SubItems[9].Text.ToString());
Basically, the codes here works like this: the program will get the data from my database and then add them to my Listview2.
(Also, can someone check if my adding items to listview2 will work? I can't run the program right now because I really have to do lots of re-coding. I'm migrating from VB.NET to C# WPF).
Going on: each variable, like userName_click will have the data from the
listview2-row-VAR2 with columns 0 to 8.
I tried replacing the subitems[n] with binding names like User from -->
(new binding("user")) but it didn't work.
by the way, here is my XAML code in listview2.
<ListView x:Name="ListView2" HorizontalAlignment="Left" Height="34" Margin="-10,446,-22,-50" VerticalAlignment="Top" Width="702" Grid.ColumnSpan="3" ClipToBounds="True">
<ListView.View>
<GridView ColumnHeaderContainerStyle="{DynamicResource CustomHeaderStyle}">
<l:FixedWidthColumn Header="username" DisplayMemberBinding="{Binding User}" FixedWidth="65"/>
<l:FixedWidthColumn Header="Host Name" DisplayMemberBinding="{Binding hostNamedb}" FixedWidth="65"/>
<l:FixedWidthColumn Header="fullname" DisplayMemberBinding="{Binding fullName}" FixedWidth="85"/>
<l:FixedWidthColumn Header="location" DisplayMemberBinding="{Binding location}" FixedWidth="65"/>
<l:FixedWidthColumn Header="department" DisplayMemberBinding="{Binding department}" FixedWidth="65"/>
<l:FixedWidthColumn Header="position" DisplayMemberBinding="{Binding position}" FixedWidth="65"/>
<l:FixedWidthColumn Header="message" DisplayMemberBinding="{Binding message}" FixedWidth="65"/>
<l:FixedWidthColumn Header="status" DisplayMemberBinding="{Binding status}" FixedWidth="65"/>
<l:FixedWidthColumn Header="id" DisplayMemberBinding="{Binding id}" FixedWidth="65"/>
<l:FixedWidthColumn Header="ip" DisplayMemberBinding="{Binding ip}" FixedWidth="65"/>
</GridView>
</ListView.View>
</ListView>
Your solution is not following, but since you know about binding I think you already know that. I can not help you making your existing code work, but instead suggest the following: Use an observable collection to hold your DB-Objects with fields like user, hostNamedb...
public DbEntry(){
user = SQLDataReader["User"].ToString();
hostNamedb = SQLDataReader["hostNamedb"].ToString();
fullName = SQLDataReader["fullName"].ToString();
location = SQLDataReader["location"].ToString();
...
}
These will fill your observable collection. Next you bind to that collection via ItemsSource in your XAML.
<ListView ItemsSource="{Binding DBObjectsCollection}">
Filling your variables with data is now only a matter of specifiying the index of the "row" from which to get the data, and getting the right field or property, e.g.
userName_click = DBObjectsCollection[2].user;
Mind you, this code will not work. This is only meant to be a basic pointer to how to solve your problem in WPF. There is already tons on information on WPF, but feel free to ask questions, should any arise.
I have a gridcontrol which is populated from database. Also, in code, I added to datatable a checkeditsettings column. I created a template in xaml , but I can't manage to convert it in C#. In my code below,
XAML code:
<dxg:GridColumn FieldName="Select" Fixed="Right" UnboundType="Boolean">
<dxg:GridColumn.EditSettings>
<dxe:CheckEditSettings />
</dxg:GridColumn.EditSettings>
<dxg:GridColumn.CellTemplate>
<DataTemplate>
<local:MyCheckEdit
IsChecked="False"
IsEnabled='True'
Checked="MyCheckEdit_Checked"
EnabledChecked="/Images/mark.png"
EnabledUnchecked="/Images/markk.png"
DisabledUnchecked="/Images/marken.png" >
</local:MyCheckEdit>
</DataTemplate>
</dxg:GridColumn.CellTemplate>
</dxg:GridColumn>
What I have tried so far:
GridColumn colselect = new GridColumn();
ComboBoxEditSettings c = new ComboBoxEditSettings();
colselect.EditSettings = c;
DataTemplate template = new DataTemplate();
template.VisualTree = new FrameworkElementFactory(typeof(MyCheckEdit));
template.VisualTree.SetBinding(MyCheckEdit.ContentProperty, new Binding("...?"));
colselect.CellTemplate = template;
I am really stack here.
Keep the DataTemplate in a Resources section in xaml, give it a name (x:Key) and just reference it from code-behind when you need it:
<dxg:DataGrid x:name="myGrid" >
<dxg:DataGrid.Resources>
<DataTemplate x:Key="MyCellTemplate" >
<local:MyCheckEdit IsChecked="False"
IsEnabled='True'
Checked="MyCheckEdit_Checked"
EnabledChecked="/Images/mark.png"
EnabledUnchecked="/Images/markk.png"
DisabledUnchecked="/Images/marken.png" />
</DataTemplate>
</dxg:DataGrid.Resources>
...
</dxg:DataGrid>
Then, in your code-behind:
GridColumn colselect = new GridColumn();
colselect.EditSettings = new ComboBoxEditSettings();
colselect.CellTemplate = myGrid.Resources["MyCellTemplate"] as DataTemplate;
I need to create a DataGridColumn from code.
The XAML equivalent would be:
<data:DataGridTemplateColumn Header="Name" Width="100">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" TextTrimming="WordEllipsis"></TextBlock>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
I've started like that:
DataGridTemplateColumn column = new DataGridTemplateColumn
{
Header = "Name",
Width = 100,
};
TextBlock inside = new TextBlock {TextTrimming = TextTrimming.CharacterEllipsis};
But I don't know how to 'merge' such puzzles. There are nested elements in XAML, how to achieve this from code?
A good way to do this is to pack the entire XAML snippet into a string and call XamlReader.Load() or XamlReader.Parse() on it. The bonus feature of this approach is that it'll work in Silverlight as well (with some fiddling), where you can't build DataTemplates in code.
Almost there, change your code to this and it should work:
DataGridTemplateColumn column = new DataGridTemplateColumn
{
Header = "Name",
Width = 100,
};
FrameworkElementFactory ftb = new FrameworkElementFactory(typeof(TextBlock));
Binding b = new Binding("Name");
ftb.SetValue(TextBlock.Text, b);
ftb.SetValue(TextBlock.TextTrimming, TextTrimming.CharacterEllipsis);
DataTemplate ct = new DataTemplate();
ct.VisualTree = ftb;
column.CellTemplate = ct;
Another method besides the above is to define your datatemplate in XAML within your resources then dynamically load it in the code:
XAML:
<Window.Resources>
<DataTemplate x:Key="myCellTemplate">
<TextBlock Text="{Binding Name}" TextTrimming="WordEllipsis" />
</DataTemplate>
</Window.Resources>
Code:
DataGridTemplateColumn column = new DataGridTemplateColumn
{
Header = "Name",
Width = 100,
};
column.CellTemplate = this.FindResource("myCellTemplate") as DataTemplate;