wpf 如何使用RowDefinition名称设置网格行?

hlswsv35  于 2022-11-26  发布在  其他
关注(0)|答案(4)|浏览(244)

我用RowDefinitionsColumnDefinition来组织我的网格,但是永远当我想在实际的任何RowDefinition之前添加一个新的RowDefinition时,我需要重新组织网格。
我看到RowDefinitionColumnDefinition有一个Name属性,所以我认为可以定义网格。行与RowDefinition名称或没有?如果是可能的,如何

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Name="RowDictTitle" Height="27"/>
        <RowDefinition Name="RowSearchWord" Height="27"/>
        <RowDefinition/>
        <RowDefinition Height="50"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>

    <!--Row 1-->
    <TextBlock Text="Word:" VerticalAlignment="Center" Margin="10,0,0,0" Grid.Row="1"/>
    <TextBox Name="Search" Grid.ColumnSpan="2" Margin="50,2,10,2"/>

    <!--Row 2-->
    <ListBox Name="Words" Grid.Row="2" Margin="10"/>
</Grid>

我想让下面

<TextBlock Text="Word" Grid.Row="RowSearchWord"/>
bkkx9g8r

bkkx9g8r1#

  • 免责声明:这个答案是一种自我宣传,在this meta post暗示的约束范围内。它宣传了一个免费的开源项目,我(在写这篇文章的时候)没有赚任何钱。唯一的收获是我知道,如果它有助于这个问题的一些未来访问者,我写所描述的控件的时间没有浪费。*

我也有同样的想法,这就是为什么不久前我编写了一个使用命名列和行的自定义网格类。
我把它放在Codeplex上,使用MIT许可证:Name-Based Grid project
使用该控件,您可以重写Xaml源代码,如下所示:

<nbg:NameBasedGrid>
    <nbg:NameBasedGrid.RowDefinitions>
        <nbg:ColumnOrRow Name="RowDictTitle" Height="27"/>
        <nbg:ColumnOrRow Name="RowSearchWord" Height="27"/>
        <nbg:ColumnOrRow Name="List"/>
        <nbg:ColumnOrRow Height="50"/>
    </nbg:NameBasedGrid.RowDefinitions>
    <nbg:NameBasedGrid.ColumnDefinitions>
        <nbg:ColumnOrRow Width="1*" Name="Left"/>
        <nbg:ColumnOrRow Width="2*" Name="Right"/>
    </nbg:NameBasedGrid.ColumnDefinitions>

    <!--Row 1-->
    <TextBlock Text="Word:" VerticalAlignment="Center" Margin="10,0,0,0" nbg:NameBasedGrid.Column="Left" nbg:NameBasedGrid.Row="RowSearchWord"/>
    <TextBox Name="Search" nbg:NameBasedGrid.Column="Left" nbg:NameBasedGrid.Row="RowDictTitle" nbg:NameBasedGrid.ExtendToColumn="Right" Margin="50,2,10,2"/>

    <!--Row 2-->
    <ListBox Name="Words" nbg:NameBasedGrid.Column="Left" nbg:NameBasedGrid.Row="List" Margin="10"/>
</nbg:NameBasedGrid>

**优点:**您将能够按名称引用列与行(包括列范围与行范围!)-不再需要计算列或行,也不再需要在布局更改时更新列范围或行范围.
**缺点:**您需要显式声明所有列和行的名称,因为NameBasedGrid中根本不支持数字引用。

5gfr0r5j

5gfr0r5j2#

好主意,但由于Grid.Row附加属性是一个整数,这是不可能的。
请参阅http://msdn.microsoft.com/en-us/library/system.windows.controls.grid.row.aspx
但是,也可以创建一个helper,它采用网格行的名称,查找行对象并返回其行索引。

ecfdbz9o

ecfdbz9o3#

我正在寻找同样的东西。找不到确切的我在寻找什么,所以我想出了我自己的解决方案使用附加属性。
我创建了一个专门的网格,附加了RowName和ColumnName属性。(在本例中,我只实现了RowName)

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

namespace GridNamedRows.CustomControl
{
    public class MyGrid: Grid
    {
        public static readonly DependencyProperty RowNameProperty =
                DependencyProperty.RegisterAttached(
                      "RowName",
                      typeof(string),
                      typeof(MyGrid),
                      new FrameworkPropertyMetadata(
                              "",
                              FrameworkPropertyMetadataOptions.AffectsParentArrange,
                              new PropertyChangedCallback(RowNameChanged)),
                      new ValidateValueCallback(IsStringNotNull));

        private static bool IsStringNotNull(object value)
        {
            return (value as string) != null;
        }

        private static void RowNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == null) 
            { 
                return; 
            }
            if (!(d is UIElement)) return;
            Grid parent = ((FrameworkElement)d).Parent as Grid;
            if (parent == null) return;
            //Find rowname
            for (int i = 0; i < parent.RowDefinitions.Count; i++)
            {
                if (parent.RowDefinitions[i].Name == e.NewValue.ToString())
                {
                    Grid.SetRow((UIElement)d, i);
                    break;
                }
            }
        }

        public static string GetRowName(DependencyObject target)
        {
            return (string)target.GetValue(RowNameProperty);
        }

        public static void SetRowName(DependencyObject target, string value)
        {
            target.SetValue(RowNameProperty, value);
        }
    }
}

可以像这样在XAML中使用它。

<Window xmlns:CustomControl="clr-namespace:GridNamedRows.CustomControl"  x:Class="GridNamedRows.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <CustomControl:MyGrid>
        <Grid.RowDefinitions>
            <RowDefinition Name="firstRow"/>
            <RowDefinition Name="secondRow"/>
            <RowDefinition Name="thirdRow"/>
        </Grid.RowDefinitions>
        <TextBox Text="one" CustomControl:MyGrid.RowName="secondRow"/>
        <TextBox Text="two" Grid.Row="2"/>
        <TextBox Text="three" CustomControl:MyGrid.RowName="firstRow"/>
    </CustomControl:MyGrid>
</Window>

它不会在设计工具中正确显示,但可以在执行阶段中运作。

cczfrluj

cczfrluj4#

沿着其他答案的思路,我提出了这个附加的属性解决方案,它不需要使用自定义的Grid
代码很大程度上是冗余的(对于行和列),可以像这样使用:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition x:Name="ThisRow"/>
        <RowDefinition x:Name="ThatRow"/>
        <RowDefinition x:Name="AnotherRow"/>
    </Grid.RowDefinitions>

    <TextBlock helpers:GridHelper.RowName="ThisRow" Text="..."/>
    <TextBlock helpers:GridHelper.RowName="AnotherRow" Text="..."/>
    <TextBlock helpers:GridHelper.RowName="ThatRow" Text="..."/>

</Grid>

网格帮助器.cs:

public class GridHelper
{
    public static string GetRowName(DependencyObject obj)
    {
        return (string)obj.GetValue(RowNameProperty);
    }

    public static void SetRowName(DependencyObject obj, string value)
    {
        obj.SetValue(RowNameProperty, value);
    }

    public static readonly DependencyProperty RowNameProperty =

        DependencyProperty.RegisterAttached("RowName", typeof(string), typeof(GridHelper), new FrameworkPropertyMetadata(string.Empty, GridHelper.OnRowNamePropertyChanged));

    public static void OnRowNamePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var name = e.NewValue?.ToString();
        if (string.IsNullOrEmpty(name)) return;

        if (!(sender is FrameworkElement fe)) return;
        if (!(fe.Parent is Grid grid)) return;

        for (int i = 0; i < grid.RowDefinitions.Count; i++)
        {
            var rd = grid.RowDefinitions[i];

            if (rd.Name.Equals(name))
            {
                Grid.SetRow(fe, i);
                return;
            }
        }

        throw new ArgumentException("Invalid RowName: " + name);
    }
    
    public static string GetColumnName(DependencyObject obj)
    {
        return (string)obj.GetValue(ColumnNameProperty);
    }

    public static void SetColumnName(DependencyObject obj, string value)
    {
        obj.SetValue(ColumnNameProperty, value);
    }

    public static readonly DependencyProperty ColumnNameProperty =

        DependencyProperty.RegisterAttached("ColumnName", typeof(string), typeof(GridHelper), new FrameworkPropertyMetadata(string.Empty, GridHelper.OnColumnNamePropertyChanged));

    public static void OnColumnNamePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var name = e.NewValue?.ToString();
        if (string.IsNullOrEmpty(name)) return;

        if (!(sender is FrameworkElement fe)) return;
        if (!(fe.Parent is Grid grid)) return;

        for (int i = 0; i < grid.ColumnDefinitions.Count; i++)
        {
            var cd = grid.ColumnDefinitions[i];

            if (cd.Name.Equals(name))
            {
                Grid.SetColumn(fe, i);
                return;
            }
        }

        throw new ArgumentException("Invalid ColumnName: " + name);
    }
}

注意:这也可能在设计器中不起作用-我从来没有尝试过使用它...

相关问题