web-dev-qa-db-ja.com

DataGridTextColumnをスタイルできないのはなぜですか?

次のコードでDataGridTextColumnのスタイルを作成しようとしました

<Style TargetType="{x:Type DataGridTextColumn}">
           ...
</Style>

ただし、Visual Studio 2010では{x:Type DataGridTextColumn}青い線で詳しく説明:Exception has been thrown by the target of an invocation.

なぜこれが発生し、どうすれば修正できますか?

38
Nike

DataGridTextColumnDataGridTextColumn(またはFrameworkElement)から派生しないため、FrameworkContentElementをスタイル設定することはできません。 FrameworkElementなどのみがスタイリングをサポートします。

FrameworkElementまたはFrameworkContentElement以外のタイプのスタイルをXAMLで作成しようとすると、そのエラーメッセージが表示されます。

これをどのように解決しますか?どんな問題でもそうですが、意志があるところに道があります。この場合、最も簡単な解決策は、DataGridの添付プロパティを作成してDataGridColumnスタイルを割り当てることです。

<DataGrid ...>
  <local:MyDataGridHelper.TextColumnStyle>
    <Style TargetType="FrameworkElement">
      ... setters here ...
    </Style>
  </local:MyDataGridHelper.TextColumnStyle>
  ...

実装は次のようなものになります。

public class MyDataGridHelper : DependencyObject
{
  // Use propa snipped to create attached TextColumnStyle with metadata:
  ... RegisterAttached("TextColumnStyle", typeof(Style), typeof(MyDataGridHelper), new PropertyMetadata
  {
    PropertyChangedCallback = (obj, e) =>
    {
      var grid = (DataGrid)obj;
      if(e.OldValue==null && e.NewValue!=null)
        grid.Columns.CollectionChanged += (obj2, e2) =>
        {
          UpdateColumnStyles(grid);
        }
    }
  }
  private void UpdateStyles(DataGrid grid)
  {
    var style = GetTextColumnStyle(grid);
    foreach(var column in grid.Columns.OfType<DataGridTextColumn>())
      foreach(var setter in style.Setters.OfType<Setter>())
        if(setter.Value is BindingBase)
          BindingOperations.SetBinding(column, setter.Property, setter.Value);
        else
          column.SetValue(setter.Property, setter.Value);
  }
}

これが機能する方法は、添付プロパティが変更されるたびに、グリッドのColumns.CollectionChangedイベントのハンドラーが追加されることです。 CollectionChangedイベントが発生すると、すべての列が設定されたスタイルで更新されます。

上記のコードは、スタイルが削除されて正常に再追加される状況を処理しないことに注意してください。2つのイベントハンドラーが登録されています。本当に堅牢なソリューションの場合は、イベントハンドラーを含む別の添付プロパティを追加してこれを修正し、イベントハンドラーを登録解除できるようにしますが、あなたの目的ではこれは重要ではないと思います。

ここでのもう1つの警告は、SetBindingとSetValueを直接使用すると、DependencyPropertyのBaseValueSourceがLocalではなくDefaultStyleになることです。これはおそらくあなたの場合には何の違いもありませんが、私はそれを言及する必要があると思いました。

26
Ray Burns

スタイルタグは適切な場所に配置する必要があります。データグリッドは現在、次のようなものです。

    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn />
        </DataGrid.Columns>
    </DataGrid>

最初に、動作しないDataGridTextColumn要素内に直接スタイルタグを追加しようとする場合があります。ただし、「DataGridTextColumn」要素内に「DataGridTextColumn.ElementStyle」または「DataGridTextColumn.EditingElementStyle」の要素を作成できます。これらの各要素タグには、スタイルタグを含めることができます。

    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn>
                <DataGridTextColumn.ElementStyle>
                    <Style TargetType="TextBlock">
                        <Setter Property="Background" Value="Green"></Setter>
                    </Style>
                </DataGridTextColumn.ElementStyle>
                <DataGridTextColumn.EditingElementStyle>
                    <Style TargetType="TextBox">
                        <Setter Property="Background" Value="Orange"></Setter>
                    </Style>
                </DataGridTextColumn.EditingElementStyle>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>

1つのスタイルは表示に適用され、もう1つのスタイルはセルが編集モードのときに適用されます。表示時のTextBlockから編集時のTextBoxに変更されることに注意してください(これにより最初に私が取得しました!)。

6
Patrick Graham

これは、レイバーンズの回答への追加です。最初は自分で実装できませんでしたが、mm8の助けを借りて( https://stackoverflow.com/a/46690951/538162 )実行しました。本当にうまくいきます。この添付されたプロパティアプローチに従うことに問題がある他の人にとっては、完全なコードスニペットが役立つかもしれません。

public class MyDataGridHelper : DependencyObject
{
    private static readonly DependencyProperty TextColumnStyleProperty = DependencyProperty.RegisterAttached("TextColumnStyle", typeof(Style), typeof(MyDataGridHelper), new PropertyMetadata
    {
        PropertyChangedCallback = (obj, e) =>
        {
            var grid = (DataGrid)obj;
            if (e.OldValue == null && e.NewValue != null)
                grid.Columns.CollectionChanged += (obj2, e2) =>
                {
                    UpdateColumnStyles(grid);
                };
        }
    });

    public static void SetTextColumnStyle(DependencyObject element, Style value)
    {
        element.SetValue(TextColumnStyleProperty, value);
    }
    public static Style GetTextColumnStyle(DependencyObject element)
    {
        return (Style)element.GetValue(TextColumnStyleProperty);
    }

    private static void UpdateColumnStyles(DataGrid grid)
    {
        var origStyle = GetTextColumnStyle(grid);
        foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
        {
            //may not add setters to a style which is already in use
            //therefore we need to create a new style merging
            //original style with setters from attached property
            var newStyle = new Style();
            newStyle.BasedOn = column.ElementStyle;
            newStyle.TargetType = origStyle.TargetType;

            foreach (var setter in origStyle.Setters.OfType<Setter>())
            {
                newStyle.Setters.Add(setter);
            }

            column.ElementStyle = newStyle;
        }
    }
}

xaml

<Grid>
    <DataGrid Name="MyDataGrid" ItemsSource="{Binding Lines}" AutoGenerateColumns="False" >
        <local:MyDataGridHelper.TextColumnStyle>
            <Style TargetType="TextBlock">
                <Setter Property="TextWrapping" Value="Wrap"/>
            </Style>
        </local:MyDataGridHelper.TextColumnStyle>
        <DataGrid.Columns>
            <DataGridTextColumn Header="ProductId1" Binding="{Binding Path=Result1}" />
            <DataGridTextColumn Header="ProductId2" Binding="{Binding Path=Result2}" />
        </DataGrid.Columns>
    </DataGrid>
</Grid>

編集:最初のアプローチでは、スタイル全体を上書きしました。新しいバージョンでは、このような他のスタイルの変更を維持することはまだ可能です

<DataGridTextColumn.ElementStyle>
    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="Foreground" Value="Red"/>
    </Style>
</DataGridTextColumn.ElementStyle>
1
pedrito