Binding to DataContext outside current ItemsSource context
Asked Answered
S

3

11

I have a DataSet bound to the Window.DataContext; I also have a DataGrid:

<DataGrid ItemsSource={Binding Tables[Items]}>
    <DataGrid.Columns>
        <DataGridTextBoxColumn Header={Binding Path=DataContext.Tables[Names]/Test, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}} />
    </DataGrid.Columns>
</DataGrid>

Basically, I'm trying to bind the Header of that column to DataTable "Names", Column "Test", first row.

However, I can't get it right. Note that I can bind it fine outside the DataGrid. The Grid's ItemsSource changes the data context and I don't know how to refer outside to the original DataContext.

It seems that the binding succeeds; but the problem is that the current item (first row) of the Tables[Names] in the Window.DataContext got lost.

If I make the DataSet static and access it via {x:Static local:dataset} then things work fine. But I can't use static datasets because there will be multiple instances (multi-user).

Can anyone please point me in the right direction?

Souter answered 13/10, 2010 at 0:34 Comment(0)
E
21

I'm pretty sure you could do what you're trying to do by using RelativeSource binding.

<DataGrid ItemsSource="{Binding StringCollection}" 
          AutoGenerateColumns="False">
  <DataGrid.Columns>
    <DataGridTextColumn Binding="{Binding}" />
    <DataGridTextColumn Binding="{
                          Binding RelativeSource={
                            RelativeSource FindAncestor, 
                            AncestorType={x:Type Window}},
                          Path=DataContext.SomethingOutsideDataContext}" />
  </DataGrid.Columns>
</DataGrid>

I made a quick example at: http://bitbucket.org/claus/wpf-bindingoutsidedatacontext

It allows you to bind to the parent Window, which in my case has the viewmodel as datacontext (with the SomethingOutsideDataContext property on it).

You should be aware though, this will only work with WPF and not Silverlight - the 'FindAncestor, AncestorType' stuff has not been implemented in Silverlight yet... I'm not sure if there's another method, besides using static resources.

Enface answered 15/12, 2010 at 13:9 Comment(4)
If it doesn't work for you, please add a little example of the structure of the Tables[Items] / Tables[Names] object you're trying to bind to, and I'll see if I can create an example using that...Enface
Also make sure that your ancestor type is a type of Window and not a type of UserControl as the DataContext property will present nullLandlordism
When I do this exact thing (but I bind Visibility instead), I get: System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DockPanel', AncestorLevel='1''. BindingExpression:Path=DataContext.IsFileMode; DataItem=null; target element is 'DataGridTextColumn' (HashCode=16991479); target property is 'Visibility' (type 'Visibility'). Currently I handle this with CodeBehind.Gallipot
@rabejens: This answer explains why this is the case and how to work around it.Glove
C
0

Don't know if this will work for you situation, but you could try something like this: 1) Give your Window a Name attribute e.g. Name=ZeWindow. 2) Bind your DataGridTextBoxColumn Header like this:

<DataGridTextBoxColumn Header="{Binding Path=DataContext.Tables[Names]/Text, ElementName=ZeWindow}"/>

So basically, instead of binding to the DataContext of the DataGrid, you bind to the DataContext of the UIElement with Name=ZeWindow.

P.S.: I'm pretty new to WPF, so this might not work with the Window, but I did something similar using UserControls

Cocksure answered 13/10, 2010 at 11:2 Comment(2)
I also tried this. It gives the same result as the FindAncestor method in my post. Ie: it binds successfully, but the "CurrentItem" of the original DataContext got lost. The culprit of my problem is that I'm trying to refer to another DataTable that has its own CurrentItem (row[0]); if I refer to just a simple property then your method as well as the FindAncestor would work fine.Souter
Ok I see, then I'm sorry but I don't know what could be the problem. Hopefully someone else will knowCocksure
W
0

This is the expected behaviour actually: The DataContext for DataGridCell is the entireRow.

so you have 3 solutions: either you add your binding in code behind like this:

in each Column's Constructor:

string source = String.Format(CultureInfo.InvariantCulture, "[{0}].", thisColumnIndex);
base.Binding = new Binding(source + "Text");

(you have to find a way to get the "thisColumnIndex". As far as I'm concerned, I add the columns right after I create them, si I simply put "dataGridOwner.Columns.Count" there).

or...

you can find a way to get the dataContext you want on each cell (tried that but it messes up badly when column/row virtualization is on)

or...

have a look there:

Binding a cell object's property to a DataGridCell in WPF DataGrid

I personally find the first one to be the better for my purpose (since I add my columns in code behind anyway), but this is really up to you in the end...


As far as columnHeaders are concerned (and only columnsHeaders, not rows), you might also explore the "DataTemplate" way:

set the columns' header to the Column itself (that way you set the column as DataContext for the header) and use a DataTemplate.

e.g.:

in each column class:

private static DependencyProperty ColumnHeaderProperty = DependencyProperty.Register("ColumnHeader", typeof(MyDataGridColumnHeader), typeof(MyTextBoxColumn));
public MyDataGridColumnHeader ColumnHeader
{
    get { return (MyDataGridColumnHeader)(GetValue(ColumnHeaderProperty)); }
    set { SetValue(ColumnHeaderProperty, value); }
}

this.ColumnHeader = new MyDataGridColumnHeader();
Header = this;

and in your dataGrid's xaml, something like:

<DataGrid.ColumnHeaderStyle>
    <Style TargetType="{x:Type DataGridColumnHeader}">
        <Style.Setters>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="VerticalContentAlignment" Value="Stretch"/>
            <Setter Property="BorderThickness" Value="{Binding BorderThickness}" />
            <Setter Property="BorderBrush" Value="{StaticResource DataGridLinesBrush}" />
            <Setter Property="Background" Value="{StaticResource DataGridColumnHeaderBackground}" />

            <Setter Property="FontFamily" Value="{Binding ColumnHeader.Font.Family, TargetNullValue={StaticResource DefaultFontFamily}}" />
            <Setter Property="FontSize" Value="{Binding ColumnHeader.Font.Size, TargetNullValue={StaticResource DefaultFontSize}}" />
            <Setter Property="FontStyle" Value="{Binding ColumnHeader.Font.Style, TargetNullValue=Normal}" />
            <Setter Property="FontWeight" Value="{Binding ColumnHeader.Font.Weight, TargetNullValue=Bold}" />
            <Setter Property="Foreground" Value="{Binding ColumnHeader.Font.Brush, TargetNullValue={StaticResource DataGridColumnHeaderForeground}}" />

            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Grid Background="{Binding ColumnHeader.Background}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <Image Name="LeftImage" Grid.Column="0" Stretch="None" Margin="3, 0, 0, 0" Source="{Binding ColumnHeader.LeftImage}" VerticalAlignment="Center"/>
                            <Image Name="RightImage" Grid.Column="2" Stretch="None" Margin="0, 0, 5, 0" Source="{Binding ColumnHeader.RightImage}" VerticalAlignment="Center"/>
                            <TextBlock Name="HeaderText"
                                       Grid.Column="1"
                                       VerticalAlignment="Center"
                                       HorizontalAlignment="Center"
                                       TextDecorations="{Binding ColumnHeader.Font.Decorations}"
                                       Text="{Binding ColumnHeader.Text}" />
                        </Grid>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style.Setters>
    </Style>
</DataGrid.ColumnHeaderStyle>

of course, my "MyDataGridColumnHeader" class contains definitions for all the properties referenced here.

hope this helps.

Woodsman answered 4/2, 2011 at 8:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.