12

我在 XAML 中有一个 WPF ListView/GridView 规范。第一列使用 CellTemplate 来指定图标,其他列使用 DisplayMemberBinding 来填充自己。图标列宽 20,图标 16,但它们被边距/填充/某些东西截断。我不知道它在哪里设置。

这是要点(我删除了一些列,因为它们是相同的):

<ListView.ItemContainerStyle>
    <Style TargetType="{x:Type ListViewItem}">
        <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
        <Setter Property="FontWeight" Value="Normal" />
        <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
                <Setter Property="FontWeight" Value="Bold" />
            </Trigger>
        </Style.Triggers>
    </Style>
</ListView.ItemContainerStyle>

<ListView.Resources>
    <DataTemplate x:Key="image">
        <Image Width="16" Height="16" Margin="0,0,0,0"
                          HorizontalAlignment="Center"
                          Source="{Binding Path=ObjectType, 
                                           Converter={StaticResource imageConverter} }" />
    </DataTemplate>
</ListView.Resources>

<ListView.View>
    <GridView>
        <GridViewColumn Width="20"
                        CellTemplate="{StaticResource image}"/>
        <GridViewColumn Width="120" Header="Name"
                        DisplayMemberBinding="{Binding Path=Name}"/>
    </GridView>
</ListView.View>

ImageConverter 只是将 ObjectType 转换为图像,因此每种类型的项目都有自己的图标。

4

6 回答 6

42

GridView 的许多功能都被硬编码到类中。特别是 GridViewRowPresenter 为每个单元格创建一个硬编码的 TextBlock 或 ContentPresenter 容器,并将边距强制为 6,0,6,0,无论您是否喜欢。

通常情况下,我会劝阻您不要进行任何涉及硬编码负边距以进行补偿的事情,因为它是分层在另一个 hack 上的 hack。但是,拆开GridViewRowPresenter后,就没有其他选择了。

我的第一次尝试是覆盖在 GridViewRowPresenter 类中创建的容器控件的隐式样式,但该控件是 TextBox 或 ContentPresenter,没有要覆盖的模板。

我的第二次尝试是反汇编代码并构建一个新的 GridViewRowPresenter。恐怕班上有太多乱伦的“内部”电话,无法使这成为一个可行的解决方案。

更聪明的架构师会提供一个 DependencyProperty,允许该类的用户覆盖为每个单元创建的容器。出于某种原因,他们允许使用标题而不是实际的单元格内容。

因此,我们得到的事实是 GridRowPresenter 中的单元格既不能被覆盖,也不能被逆向工程,也不能被模板化。很抱歉,负边距是这门课所能做到的最好的。

我们需要一个更好的 ListView:GridView 不适合不美观的界面,尽管我很欣赏他们最初的架构师试图通过将 ItemsPresenter 从演示文稿中分离出来的做法。

于 2010-08-05T13:44:13.847 回答
10

默认情况下,每个单元格的硬编码边距为 6,0。可能这就是你的问题。您可以通过在单元格模板中设置 -6,0 的边距来覆盖此行为

于 2009-11-10T19:51:24.623 回答
6

使用Snoop找出哪个元素负责施加额外的间距。将鼠标悬停在空间上并按住 control 和 shift。然后该元素将在可视树中突出显示。

于 2009-08-29T15:40:56.403 回答
2

我基于 Ian Griffiths 的 hack(http://www.interact-sw.co.uk/iangblog/2007/05/30/wpf-listview-column-margins)开发了一个解决方案。该控件执行以下操作:

  • 将 GridViewRowPresenter 创建的 TextBlock 或 ContentPresenter 的硬编码 Margin 设置为此控件的 Padding 依赖项属性
  • 将此控件的 Horizo​​ntalAlignment 和 VerticalAlignment 传播到其容器
  • 重置 ListViewItem/GridViewRowPresenter 容器的边距/填充。

因此,它为您提供了一种对硬编码值的控制。

代码:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Put.Your.Namespace.Here
{
   /// <summary>
   /// Class allows for reseting hard coded ListViewItem margins and paddings
   /// </summary>
   public class ListViewCustomizableCellPresenter : Decorator
   {
      protected override void OnVisualParentChanged(DependencyObject oldParent)
      {
         base.OnVisualParentChanged(oldParent);

         var cp = VisualTreeHelper.GetParent(this) as FrameworkElement;
         if (cp != null)
         {
            cp.Margin = Padding;
            cp.VerticalAlignment = VerticalAlignment;
            cp.HorizontalAlignment = HorizontalAlignment;
         }

         ResetGridViewRowPresenterMargin();
         ResetListViewItemPadding();
      }

      private T FindInVisualTreeUp<T>() where T : class
      {
         DependencyObject result = this;
         do
         {
            result = VisualTreeHelper.GetParent(result);
         }
         while (result != null && !(result is T));
         return result as T;
      }

      private void ResetGridViewRowPresenterMargin()
      {
         var gvrp = FindInVisualTreeUp<GridViewRowPresenter>();
         if (gvrp != null)
            gvrp.Margin = new Thickness(0);
      }

      private void ResetListViewItemPadding()
      {
         var lvi = FindInVisualTreeUp<ListViewItem>();
         if (lvi != null)
            lvi.Padding = new Thickness(0);
      }

      /// <summary>
      /// Padding dependency property registration
      /// </summary>
      public static readonly DependencyProperty PaddingProperty =
         DependencyProperty.Register("Padding", typeof(Thickness), typeof(ListViewCustomizableCellPresenter), new PropertyMetadata(default(Thickness)));

      /// <summary>
      /// Padding dependency property
      /// </summary>
      public Thickness Padding
      {
         get { return (Thickness)GetValue(PaddingProperty); }
         set { SetValue(PaddingProperty, value); }
      }
   }

}

xaml 中的示例用法(GridViewColumn 的内部定义):

<GridViewColumn.CellTemplate>
    <DataTemplate>
        <yourxamlnamespacehere:ListViewCustomizableCellPresenter Padding="0"
                                                        VerticalAlignment="Center">
            <YourControlOrPanelHere />
        </yourxamlnamespacehere:ListViewCustomizableCellPresenter>
    </DataTemplate>
</GridViewColumn.CellTemplate>
于 2013-01-17T15:53:14.607 回答
2

由于边距是硬编码常量,唯一的解决方案是使用反射手动更改它。

在 VB 中:

' Hack : 
' Changes the default margin for the GridView's Column.
Dim GridViewCellMarginProperty = GetType(GridViewRowPresenter).GetField("_defalutCellMargin", BindingFlags.NonPublic Or BindingFlags.Static Or BindingFlags.GetField)
If (GridViewCellMarginProperty IsNot Nothing) Then
    GridViewCellMarginProperty.SetValue(Nothing, New Thickness(2, 0, 2, 0))
End If

它在我的项目上运行良好。

于 2014-07-08T16:48:35.437 回答
0

我找到的解决方案是在添加子项时继承GridViewRowPresenter并覆盖OnVisualChildrenChanged以将边距设置为 0:

public class MyGridViewRowPresenter : GridViewRowPresenter
{
    protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
    {
        if (visualAdded is FrameworkElement frameworkElement)
            frameworkElement.Margin = new Thickness(0);
        base.OnVisualChildrenChanged(visualAdded, visualRemoved);
    }
}
于 2021-10-20T15:10:24.650 回答