3
votes

I'm using the WPF combobox for filtering the items but I decide to put it in my datagrid instead but i can't get it to work probably in their i can only get it to work when is outside of the datagrid.

I think the problem is because

RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, or ElementName=win

isn't supported inside the datagrid so how do I getting it to work.

this is the error I get

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=SelectedParam; DataItem=null; target element is 'ComboBox' (Name='Krydsmålbox'); target property is 'SelectedValue' (type 'Object')

<DataGrid x:Name="hjuldata" 
ItemsSource="{Binding Source={StaticResource cvsTasks}}"
CanUserAddRows="False"  BorderBrush="#FF303030" Foreground="#FF00FB0B" Background="#FF303030" AutoGenerateColumns="False" GridLinesVisibility="None" VerticalAlignment="Center" Height="644" Canvas.Left="20" Canvas.Top="257" Width="1250" >
 <DataGrid.Columns>
 <DataGridTextColumn Binding="{Binding Krydsmålet}">
 <DataGridTextColumn.HeaderTemplate>
     <DataTemplate>
        <ContentPresenter Content="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
     </DataTemplate>
 </DataGridTextColumn.HeaderTemplate>
  <DataGridTextColumn.Header>
      <ComboBox   x:Name="Krydsmålbox" Foreground="#FFEAEAEA" Background="#FF303030" FontSize="12" 
                  Style="{StaticResource ComboBoxTest2}"  ItemTemplate="{StaticResource cmbTemplate2}" 
                  ItemsSource="{Binding}"  SelectedValuePath="Krydsmålene"
                  SelectedValue = "{Binding SelectedParam, RelativeSource={RelativeSource FindAncestor, 
                  AncestorType={x:Type Window}},UpdateSourceTrigger=PropertyChanged}" BorderBrush="#FF303030" Height="40" DockPanel.Dock="Top" IsSynchronizedWithCurrentItem="True" SelectionChanged="Krydsmålbox_SelectionChanged" Canvas.Left="813" Canvas.Top="96" Width="146"/>
 </DataGridTextColumn.Header>
 </DataGridTextColumn>

data template

<DataTemplate x:Key="cmbTemplate2">
    <WrapPanel Margin="0 5 0 5" Height="30">
        <Image Width="10" Height="20" Stretch="Fill" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,0,15,0"/>
        <Label Content="{Binding Krydsmålene}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16" Foreground="#FF00FB0B"/>
    </WrapPanel>
</DataTemplate>

CS

public event PropertyChangedEventHandler PropertyChanged;

[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public string SelectedParam
{
    get { return _selectedParam; }
    set
    {
        _selectedParam = value; OnPropertyChanged("SelectedParam");
        if (_selectedParam == "Krydsmål") { BindData(); } else { hjuldata.ItemsSource = FilterKategori().Tables[0].DefaultView; ; }
    }
}

public DataSet DTbindkryds()
{
    Data = @"SELECT Krydsmålene FROM Data.Krydsmål";
    //SQL statement to fetch entries from Krydsmål
    DataSet dsdata = new DataSet();

    //Open SQL Connection
    using (conn = new SqlConnection(connStrings))
    {
        conn.Open();

        //Initialize command object
        using (conn = new SqlConnection(connStrings))
        using (cmd = new SqlCommand(Data, conn))
        {
            SqlDataAdapter adapters = new SqlDataAdapter(cmd);

            //Fill the result set
            adapters.Fill(dsdata);
            conn.Close();
        }
    }
    return dsdata;
}

private void bindkrydsmål()
{
    Krydsmålbox.ItemsSource = DTbindkryds().Tables[0].DefaultView;
}
2
Why don't you define a data template for the grid and define a data type so that you could bind the selected value directly to some property?ViVi
becuase i couldn't get datatemplate to work with the header propertyomini data
It's possible. Search for wpf datagrid header templateViVi
i had tryed using <DataGridTextColumn.Header> <DataTemplate> <ComboBox </DataTemplate> then it says system.windows.datatemplate in my combobox and my combobox don't exists in the curren't contextomini data
Problem is with your Binding, your Path should point to DataContext.SelectedParam.XAMlMAX

2 Answers

0
votes

I think your real problem is very small and frequent with WPF developers. Change your SelectedValue binding like below:

    SelectedValue = "{Binding DataContext.SelectedParam, RelativeSource={RelativeSource FindAncestor, 
                       AncestorType={x:Type Window}},UpdateSourceTrigger=PropertyChanged}"

As you have not given the code how you are assigning the DataContext for window, it's a viable option to check for. If it's the same window class then binding should work without DataContext. but the assumption that RelativeSource binding is not supported inside the datagrid is wrong. RelativeSource will work as long as element is in VisualTree of control and your combo box is in visualtree of window. Right?

To save time on proving the fact, below is the demo (created by using maximum of your code reuse and using template in header of DataGrid).

XAML:

<DataGrid ItemsSource="{Binding Items}" CanUserAddRows="False" AutoGenerateColumns="False" VerticalAlignment="Stretch" >
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding DeviceState}">                
            <DataGridTextColumn.Header>
                <ComboBox HorizontalAlignment="Stretch" Width="200" SelectedValuePath="DeviceState"
                                ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.Items}"
                                SelectedValue = "{Binding SelectedParam, RelativeSource={RelativeSource FindAncestor, 
                                AncestorType={x:Type Window}}}" >
                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <Label Content="{Binding DeviceState}" VerticalAlignment="Center" HorizontalAlignment="Stretch"/>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                </ComboBox>                                    
            </DataGridTextColumn.Header>
        </DataGridTextColumn>
        <DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="200"/>
    </DataGrid.Columns>
</DataGrid>

Code Behind:

 public MainWindow()
    {
        Items = new List<Device>();
        Items.Add(new Device() { Name = "Device1",DeviceState = 1 });
        Items.Add(new Device() { Name = "Device2", DeviceState = 2 });
        Items.Add(new Device() { Name = "Device3", DeviceState = 3 });
        Items.Add(new Device() { Name = "Device4", DeviceState = 4 });
        Items.Add(new Device() { Name = "Device5", DeviceState = 5 });
        InitializeComponent();
        
    }

    public List<Device> Items { get; set; }

    private string _selectedParam = "1";
    public string SelectedParam
    {
        get { return _selectedParam; }
        set
        {
            _selectedParam = value; 
            UpdateProperty("SelectedParam");                
        }
    }

Output:

Combobox in datagrid header

As you can see in output that is a combobox in header of datagrid has a selected item by default and that is reflected using relativesource binding as you can see in above XAML.

4
votes

Don't use a DataTemplate in the Header property, use it in the HeaderTemplate like so:

<DataGridTextColumn.HeaderTemplate>
  <DataTemplate>
    <ComboBox x:Name="Krydsmålbox" Foreground="#FFEAEAEA" Background="#FF303030" FontSize="12" 
    Style="{StaticResource ComboBoxTest2}"  ItemTemplate="{StaticResource cmbTemplate2}" 
    ItemsSource="{Binding}"  SelectedValuePath="Krydsmålene"
    SelectedValue = "{Binding SelectedParam, RelativeSource={RelativeSource FindAncestor, 
    AncestorType={x:Type Window}},UpdateSourceTrigger=PropertyChanged}" BorderBrush="#FF303030" Height="40" DockPanel.Dock="Top" IsSynchronizedWithCurrentItem="True" SelectionChanged="Krydsmålbox_SelectionChanged" Canvas.Left="813" Canvas.Top="96" Width="146"/>
  </DataTemplate>
</DataGridTextColumn.HeaderTemplate>

Once you do so, both ElementName and RelativeSource should work.

If you want to be able to refer to the combobox by name in the code behind, you can use a ContentPresenter to present whatever kind of Header you want (I think the default HeaderTemplate is a TextBlock instead of a ContentPresenter):

<DataGridTextColumn.HeaderTemplate>
  <DataTemplate>
    <ContentPresenter Content="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
  </DataTemplate>
</DataGridTextColumn.HeaderTemplate>
<DataGridTextColumn.Header>
  <ComboBox x:Name="Krydsmålbox" Foreground="#FFEAEAEA" Background="#FF303030" FontSize="12" 
      Style="{StaticResource ComboBoxTest2}"  ItemTemplate="{StaticResource cmbTemplate2}" 
      ItemsSource="{Binding}"  SelectedValuePath="Krydsmålene"
      SelectedValue = "{Binding SelectedParam, RelativeSource={RelativeSource FindAncestor, 
      AncestorType={x:Type Window}},UpdateSourceTrigger=PropertyChanged}" BorderBrush="#FF303030" Height="40" DockPanel.Dock="Top" IsSynchronizedWithCurrentItem="True" SelectionChanged="Krydsmålbox_SelectionChanged" Canvas.Left="813" Canvas.Top="96" Width="146"/>
</DataGridTextColumn.Header>