Russ Suter
asked on
How do I bind a property in code behind given only the name of that property?
I'm trying to figure out a way to bind a property to a DataGrid cell given the name of the property as a string. I sure hope I am about to explain this sufficiently to get my point across. Here goes...
In my user control I have this property:
I'm building a DataGrid control that will let me update values similar to a Visual Studio property grid. Right now I have the following code:
In my user control I have this property:
public ContactInfo CurrentContact
{
get
{
return (ContactInfo)GetValue(CurrentContactProperty);
}
set
{
if (!Equals((ContactInfo)GetValue(CurrentContactProperty), value))
{
PropertyChangeAndNotify(CurrentContactProperty, value);
}
}
}
public static readonly DependencyProperty CurrentContactProperty = DependencyProperty.Register("CurrentContact", typeof(ContactInfo), typeof(MyUserControl), new FrameworkPropertyMetadata(null));
The class ContactInfo is a basic as you would expect contact type of class with stuff like first name, last name, address, city, phone number, etc...I'm building a DataGrid control that will let me update values similar to a Visual Studio property grid. Right now I have the following code:
List<ExtendedPropertyDefinition> implementedProperties = ContactInfo.ImplementedProperties();
foreach (ExtendedPropertyDefinition item in implementedProperties)
{
PropertyInfo propInfo = typeof(ContactInfo).GetProperty(item.Name);
if (propInfo != null)
{
item.Value = propInfo.GetValue(this.CurrentContact, null);
}
}
The idea here is that I only want to bind the property if there's an entry in Implemented properties (which comes from a table in a database). That table contains info about the property including it's name. What I have above works fine for populating the DataGrid with values but it doesn't support any binding. I need to be able to edit the value in the grid and have the value update elsewhere in my application. This works in other places but I'm struggling to figure out what to do here.
ASKER
I've been struggling with this since yesterday evening. This looks like it has promise but the problem I'm now up against is the 3rd step where the binding method is set. The issue is that I'm using the DataGrid's ItemsSource property to actually setup the grid. It's much faster and simpler than doing it manually. Once everything has had a chance to settle down I can totally see and manipulate each row just fine. Unfortunately, I haven't yet found the DataGrid event that fires after the rows have been created after the ItemsSource property has been set. Any help there?
I'm trying to determine how these pieces are tying together.
Are you trying to basically loop back through the DataGrid's items/rows and manually apply the binding to each cell as appropriate?
If so, is there a reason you don't simply set the data binding on the column at design-time, e.g.:
Are you trying to basically loop back through the DataGrid's items/rows and manually apply the binding to each cell as appropriate?
If so, is there a reason you don't simply set the data binding on the column at design-time, e.g.:
<DataGrid ItemsSource="{Binding AllContacts}" AutoGenerateColumns="False" ...>
<DataGrid.Columns>
<!-- A typical text column that is bound to the FirstName property on each row -->
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" Width="SizeToCells" MinWidth="120">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.VerticalAlignment" Value="Center"/>
<Setter Property="TextBlock.Margin" Value="4"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<!-- A custom column that implements some input so you can modify the row's value within the grid -->
<DataGridTemplateColumn Header="Custom Column">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!-- You can stuff any XAML you want in here - it would apply to every row -->
<CheckBox
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsChecked="{Binding Path=Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Checked="CheckBox_Checked"
Unchecked="CheckBox_Checked" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ASKER
Honestly at this stage the only reason I'm not doing something is because I haven't yet learned that it's possible. I'm still deep in my learning cycle.
DataGridTemplateColumns look like they might be useful but I still don't fully understand how to select the appropriate one for the data type of the value being assigned. Also, I'm struggling to understand the difference between CellTemplate and CellEditTemplate and how they interact. More research needed.
DataGridTemplateColumns look like they might be useful but I still don't fully understand how to select the appropriate one for the data type of the value being assigned. Also, I'm struggling to understand the difference between CellTemplate and CellEditTemplate and how they interact. More research needed.
ASKER
I'm largely avoiding doing this with bindings because in my experience it slows things down too much. I'm trying to do this the hard way (because it's noticeably faster).
So DataGridTemplateColumn is there when none of the existing column types (DataGridCheckBoxColumn, DataGridComboBoxColumn, DataGridHyperlinkColumn, or DataGridTextColumn) are exactly what you need. DataGridTemplateColumn is the "build-it-yourself" type, which is great for situations like putting an image or icons, or a more elaborate custom control into each row.
I personally am not a fan of some of the default input columns and find it easier to use the template column to have more control over the input elements.
As far as CellEditingTemplate vs. CellTemplate, it's just defining two different ways a cell will look, depending on whether you are (as a user at runtime) editing that cell value or not. If you go to the documentation and scroll down to the screenshot:
https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.datagridtemplatecolumn.celleditingtemplate?view=netframework-4.8
...you'll see that in normal "CellTemplate" mode, you see a calendar icon representing the value, while the CellEditingTemplate contains a editable date picker. Once you leave the edit mode, it goes back to the CellTemplate.
I personally am not a fan of some of the default input columns and find it easier to use the template column to have more control over the input elements.
As far as CellEditingTemplate vs. CellTemplate, it's just defining two different ways a cell will look, depending on whether you are (as a user at runtime) editing that cell value or not. If you go to the documentation and scroll down to the screenshot:
https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.datagridtemplatecolumn.celleditingtemplate?view=netframework-4.8
...you'll see that in normal "CellTemplate" mode, you see a calendar icon representing the value, while the CellEditingTemplate contains a editable date picker. Once you leave the edit mode, it goes back to the CellTemplate.
ASKER
That helps. Thanks. The remaining question is how do I select a different template based on the data type that I'm looking at?
For example, if the cell contains a string data type then I just want to use a TextBlock for the view and a TextBox for edit. But if the cell contains a boolean data type then I want to use a Checkbox for view and edit.
For example, if the cell contains a string data type then I just want to use a TextBlock for the view and a TextBox for edit. But if the cell contains a boolean data type then I want to use a Checkbox for view and edit.
ASKER
Also worth noting, It appears I can use binding within the grid. I just can't set the DataContext of my user control. That's when the performance goes downhill.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
https://www.wpf-tutorial.com/data-binding/data-binding-via-code-behind/
...and just adding my comments:
First, create a new Binding object and pass in the SOURCE property name:
Open in new window
Second, set Source to be the source OBJECT:Open in new window
Finally, on the TARGET object, call the SetBinding method, specifying the property that should be receiving the data, and then passing in the binding object you created:Open in new window
The above would be the same as doing this in XAML:
Open in new window
The most common issue I face with data-binding is having code or situations that directly set the value of the bound property, which breaks the binding. In other words, let's say you have something like this in your XAML:
<TextBlock x:Name="txtLabel" Text="{Binding StatusMessage}" />
Now let's say that somewhere in your code, you have:
txtLabel.Value = "123";
Whenever you directly set the value of a property that has a binding, the binding will be removed in favor of the direct value.
Normally, this isn't a huge problem but it does happen, and it can happen more frequently with design-time properties (DependencyProperty.Regist
1. Add the diagnostics namespace to the top of the control:
<Window x:Class="...blah blah..."
xmlns:diag="clr-namespace:
2. Add the diagnostic trace to the binding:
<TextBlock x:Name="txtLabel" Text="{Binding StatusMessage, diag:PresentationTraceSour
3. Run your program and watch the output.
Some extra general tips/techniques on data binding debugging:
https://spin.atomicobject.com/2013/12/11/wpf-data-binding-debug/