Reputation: 392
What i am trying to do is make a CustomDatagrid which has already 2 fixed columns. Then i can re-use this CustomDatagrid and add extra columns to fit my best purpose. But when i do add extra columns i would like to be able to resize the 2 fixed columns. I tried working it out with dependency properties as the example underneath but to no avail. And i do not get any binding errors whatsoever which leaves me without any idea what is wrong.
=> this small example will clarify what i am trying to do
CustomDataGrid.Xaml
<DataGrid x:Class="DataGridWidthTestControl.CustomDataGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DataGridWidthTestControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<DataGrid.Columns>
<DataGridTextColumn Width="{Binding Column1Width}" Header="Column1"/>
<DataGridTextColumn Width="{Binding Column2Width}" Header="Column2"/>
</DataGrid.Columns>
CustomDataGrid.Xaml.CS - Codebehind
namespace DataGridWidthTestControl
{
/// <summary>
/// Interaction logic for CustomDataGrid.xaml
/// </summary>
public partial class CustomDataGrid : DataGrid
{
public CustomDataGrid()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty Column1WidthProperty = DependencyProperty.Register( "Column1Width", typeof(DataGridLength), typeof(CustomDataGrid), new FrameworkPropertyMetadata(new DataGridLength(25, DataGridLengthUnitType.Star)));
public DataGridLength Column1Width
{
get { return (DataGridLength)GetValue(Column1WidthProperty); }
set { SetValue(Column1WidthProperty, value); }
}
public static readonly DependencyProperty Column2WidthProperty = DependencyProperty.Register("Column2Width", typeof(DataGridLength), typeof(CustomDataGrid), new FrameworkPropertyMetadata(new DataGridLength(15, DataGridLengthUnitType.Star)));
public DataGridLength Column2Width
{
get { return (DataGridLength)GetValue(Column2WidthProperty); }
set { SetValue(Column2WidthProperty, value); }
}
}
}
Mainwindow.xaml (codebehind is empty except for default initialize call)
<Window x:Class="DataGridWidthTestControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataGridWidthTestControl"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:CustomDataGrid Column1Width="1*" Column2Width="1*" >
<DataGrid.Columns>
<DataGridTextColumn Width="10*" Header="column3"/>
</DataGrid.Columns>
</local:CustomDataGrid>
</Grid>
</Window>
Upvotes: 1
Views: 85
Reputation: 10349
First of all you need to know that bindings without any source specified (like in your case), bindings using Binding.RelativeSource
and bindings using Binding.ElementName
will not work with DataGridColumn
, because it derives directly from DependencyObject
, which is not sufficient. Basically, in order for those to work, the target object needs to be of type deriving from FrameworkElement
or FrameworkContentElement
, and it should be part of the visual or logical tree (which DataGridColumn
s are not). Be aware though that there are few exceptions to that requirement (like Freezable
s defined within resource dictionaries), and more to come with upcoming versions of the framework.
So unfortunately you need to explicitly specify Binding.Source
(which should be an instance of your CustomDataGrid
class). I can't think of any way of doing it in XAML (Source={x:Reference (...)}
is not applicable because you cannot reference an object from within its definition), so I think you'll need to fallback to code-behind (which is not unusual when devising custom controls).
The easiest way is to name your columns:
<DataGrid.Columns>
<DataGridTextColumn x:Name="Column1" x:FieldModifier="private" Header="Column1" />
<DataGridTextColumn x:Name="Column2" x:FieldModifier="private" Header="Column2" />
</DataGrid.Columns>
(x:FieldModifier="private"
is optional) and then set the bindings upon initialization of your control:
public CustomDataGrid()
{
InitializeComponent();
BindingOperations.SetBinding(Column1, DataGridColumn.WidthProperty, new Binding
{
Path = new PropertyPath(Column1WidthProperty),
Source = this,
});
BindingOperations.SetBinding(Column2, DataGridColumn.WidthProperty, new Binding
{
Path = new PropertyPath(Column2WidthProperty),
Source = this,
});
}
You also may want to add Mode = BindingMode.TwoWay
to the bindings so that the values on your control stay in sync if the user resizes the columns manually.
Note that I intentionally removed the DataContext = this
line because a) it is not necessary and b) it would give you a lot of trouble when using your control (e.g. binding DataGrid.ItemsSource
to a view-model property would not work as it usually does).
Upvotes: 1
Reputation: 851
You'll need to programmatically insert the fixed columns in your CustomDataGrid class instead of specifying them in the template.
Something like:
public override void OnApplyTemplate()
{
if (!this.Columns.Any(c => c.Header.ToString() == "Column1"))
{
this.Columns.Insert(0,
new DataGridTextColumn
{
Width = this.Column1Width,
Header = "Column1"
});
}
if (!this.Columns.Any(c => c.Header.ToString() == "Column2"))
{
this.Columns.Insert(1,
new DataGridTextColumn
{
Width = this.Column2Width,
Header = "Column2"
});
}
base.OnApplyTemplate();
}
I'm not sure OnApplyTemplate() is the right time to do it, there might be a better method to override, but this is the concept I'd go for to make this work.
Upvotes: 2