完成Grid示例

This commit is contained in:
zengwenjie
2025-09-30 08:21:01 +08:00
parent a023c7f6cb
commit 61117b2098
6 changed files with 246 additions and 4 deletions

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows;
namespace RazorEngineTest
{
public static class ExHelper
{
public static T? FindVisualChild<T>(this DependencyObject parent, string name = "") where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T result)
{
if (string.IsNullOrEmpty(name)) return result;
else if (result is FrameworkElement element && element.Name == name) return result;
}
var descendant = child.FindVisualChild<T>(name);
if (descendant != null) return descendant;
}
return null;
}
}
}

View File

@@ -5,22 +5,37 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RazorEngineTest" xmlns:local="clr-namespace:RazorEngineTest"
mc:Ignorable="d" mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"> Title="MainWindow" Height="500" Width="800">
<Grid> <Grid>
<Border BorderBrush="Green" BorderThickness="2" VerticalAlignment="Top" HorizontalAlignment="Left"> <Border BorderBrush="Green" BorderThickness="2" VerticalAlignment="Top" HorizontalAlignment="Left">
<local:MyControl Width="200" Height="40"/> <local:MyControl Width="200" Height="40"/>
</Border> </Border>
<Path Width="24" Height="24" Fill="Orange" Data="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-8.512 8.272l5.532 3.243-.686 1.162-5.533-3.243.687-1.162zm-1.456 3.113l6.185 1.739-.332 1.23-6.204-1.667.351-1.302zm-.672 2.813l6.498.65-.117 1.28-6.504-.586.123-1.344zm-.193 2.469h6.667v1.333h-6.667v-1.333zm8.833 3.333h-11v-7h1v6h9v-6h1v7zm-.852-8.704l-3.56-5.219 1.115-.76 3.559 5.219-1.114.76zm1.356-.841l-1.08-6.224 1.328-.231 1.082 6.224-1.33.231z"/> <Path Width="24" Height="24" HorizontalAlignment="Left" Fill="Orange" Data="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-8.512 8.272l5.532 3.243-.686 1.162-5.533-3.243.687-1.162zm-1.456 3.113l6.185 1.739-.332 1.23-6.204-1.667.351-1.302zm-.672 2.813l6.498.65-.117 1.28-6.504-.586.123-1.344zm-.193 2.469h6.667v1.333h-6.667v-1.333zm8.833 3.333h-11v-7h1v6h9v-6h1v7zm-.852-8.704l-3.56-5.219 1.115-.76 3.559 5.219-1.114.76zm1.356-.841l-1.08-6.224 1.328-.231 1.082 6.224-1.33.231z"/>
<Button HorizontalAlignment="Right" VerticalAlignment="Top" Background="Transparent" BorderThickness="0" Foreground="Green"> <Button HorizontalAlignment="Right" VerticalAlignment="Top" Background="Transparent" BorderThickness="0" Foreground="Green" Click="Button_Click">
<Image Width="64" Height="64" HorizontalAlignment="Left"> <Image Width="64" Height="64" HorizontalAlignment="Left">
<Image.Source> <Image.Source>
<DrawingImage> <DrawingImage>
<DrawingImage.Drawing> <DrawingImage.Drawing>
<GeometryDrawing Brush="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}},Path=Foreground}" Geometry="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-8.512 8.272l5.532 3.243-.686 1.162-5.533-3.243.687-1.162zm-1.456 3.113l6.185 1.739-.332 1.23-6.204-1.667.351-1.302zm-.672 2.813l6.498.65-.117 1.28-6.504-.586.123-1.344zm-.193 2.469h6.667v1.333h-6.667v-1.333zm8.833 3.333h-11v-7h1v6h9v-6h1v7zm-.852-8.704l-3.56-5.219 1.115-.76 3.559 5.219-1.114.76zm1.356-.841l-1.08-6.224 1.328-.231 1.082 6.224-1.33.231z"/> <GeometryDrawing Brush="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}},Path=Foreground}"
Geometry="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-8.512 8.272l5.532 3.243-.686 1.162-5.533-3.243.687-1.162zm-1.456 3.113l6.185 1.739-.332 1.23-6.204-1.667.351-1.302zm-.672 2.813l6.498.65-.117 1.28-6.504-.586.123-1.344zm-.193 2.469h6.667v1.333h-6.667v-1.333zm8.833 3.333h-11v-7h1v6h9v-6h1v7zm-.852-8.704l-3.56-5.219 1.115-.76 3.559 5.219-1.114.76zm1.356-.841l-1.08-6.224 1.328-.231 1.082 6.224-1.33.231z"/>
</DrawingImage.Drawing> </DrawingImage.Drawing>
</DrawingImage> </DrawingImage>
</Image.Source> </Image.Source>
</Image> </Image>
</Button> </Button>
<Border BorderBrush="Red" BorderThickness="2" Width="400" Height="400" HorizontalAlignment="Right" VerticalAlignment="Bottom">
<local:MyGrid x:Name="myGrid">
<local:MyGrid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</local:MyGrid.RowDefinitions>
<local:MyGrid.ColDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</local:MyGrid.ColDefinitions>
<Border Background="Green"/>
<Border Grid.RowSpan="2" Width="20" HorizontalAlignment="Right" Background="Blue" Opacity=".5"/>
</local:MyGrid>
</Border>
</Grid> </Grid>
</Window> </Window>

View File

@@ -20,5 +20,13 @@ namespace RazorEngineTest
{ {
InitializeComponent(); InitializeComponent();
} }
private void Button_Click(object sender, RoutedEventArgs e)
{
Button c1 = new Button() { Content = "Button", Background = Brushes.Red, HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch };
Grid.SetColumn(c1, 1);
Grid.SetRow(c1, 1);
this.myGrid.Children.Add(c1);
}
} }
} }

155
RazorEngineTest/MyGrid.cs Normal file
View File

@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace RazorEngineTest
{
[ContentProperty("Children")]
[TemplatePart(Name = "PART_ChildrenContainer", Type = typeof(ItemsControl))]
public class MyGrid : Control
{
[AllowNull]
private ItemsControl PART_ChildrenContainer;
[AllowNull]
private Grid ChildrenContainer;
static MyGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyGrid), new FrameworkPropertyMetadata(typeof(MyGrid)));
}
public MyGrid()
{
this.Children = new UICollection();
this.RowDefinitions = new RowDefinitions();
this.ColDefinitions = new ColDefinitions();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.PART_ChildrenContainer = this.GetTemplateChild("PART_ChildrenContainer") as ItemsControl;
if (this.PART_ChildrenContainer != null)
this.PART_ChildrenContainer.Loaded += PART_ChildrenContainer_Loaded;
else throw new ArgumentNullException($"「{nameof(MyGrid)}」模版必须定义名称为「PART_ChildrenContainer」的「ItemsControl」类型子元素容器...");
}
private void PART_ChildrenContainer_Loaded(object sender, RoutedEventArgs e)
{
this.ChildrenContainer = this.PART_ChildrenContainer.FindVisualChild<Grid>("ChildrenContainer");
if (this.ChildrenContainer != null)
{
this.ChildrenContainer.ShowGridLines = DesignerProperties.GetIsInDesignMode(this);
this.ChildrenContainer.RowDefinitions.Clear();
foreach (var row in this.RowDefinitions) this.ChildrenContainer.RowDefinitions.Add(row);
this.ChildrenContainer.ColumnDefinitions.Clear();
foreach (var col in this.ColDefinitions) this.ChildrenContainer.ColumnDefinitions.Add(col);
}
}
/// <summary>
/// 子元素集合
/// </summary>
public UICollection Children
{
get { return (UICollection)GetValue(ChildrenProperty); }
set { SetValue(ChildrenProperty, value); }
}
public static readonly DependencyProperty ChildrenProperty =
DependencyProperty.Register("Children", typeof(UICollection), typeof(MyGrid), new PropertyMetadata(null,
(d, e) => (d as MyGrid)?.Children_PropertyChangedCallback(e)));
/// <summary>
/// 处理「MyGrid.Children」属性变更
/// </summary>
protected virtual void Children_PropertyChangedCallback(DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is UICollection oldUIColl) oldUIColl.CollectionChanged -= UICollection_CollectionChanged;
if (e.NewValue is UICollection newUIColl) newUIColl.CollectionChanged += UICollection_CollectionChanged;
}
protected virtual void UICollection_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//TODO这里可以处理逻辑树的父子级关系
}
/// <summary>
/// 行定义
/// </summary>
public RowDefinitions RowDefinitions
{
get { return (RowDefinitions)GetValue(RowDefinitionsProperty); }
protected internal set { SetValue(RowDefinitionsPropertyKey, value); }
}
public static readonly DependencyPropertyKey RowDefinitionsPropertyKey =
DependencyProperty.RegisterReadOnly("RowDefinitions", typeof(RowDefinitions), typeof(MyGrid), new PropertyMetadata(null,
(d, e) => (d as MyGrid)?.RowDefinitions_PropertyChangedCallback(e)));
public static readonly DependencyProperty RowDefinitionsProperty = RowDefinitionsPropertyKey.DependencyProperty;
/// <summary>
/// 处理「MyGrid.RowDefinitions」属性变更
/// </summary>
protected virtual void RowDefinitions_PropertyChangedCallback(DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is RowDefinitions oldRows) oldRows.CollectionChanged -= Rows_CollectionChanged;
if (e.NewValue is RowDefinitions newRows) newRows.CollectionChanged += Rows_CollectionChanged;
}
private void Rows_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (this.ChildrenContainer != null)
{
this.ChildrenContainer.RowDefinitions.Clear();
foreach (var row in this.RowDefinitions)
this.ChildrenContainer.RowDefinitions.Add(row);
}
}
/// <summary>
/// 列定义
/// </summary>
public ColDefinitions ColDefinitions
{
get { return (ColDefinitions)GetValue(ColDefinitionsProperty); }
protected internal set { SetValue(ColDefinitionsPropertyKey, value); }
}
public static readonly DependencyPropertyKey ColDefinitionsPropertyKey =
DependencyProperty.RegisterReadOnly("ColDefinitions", typeof(ColDefinitions), typeof(MyGrid), new PropertyMetadata(null,
(d, e) => (d as MyGrid)?.ColDefinitions_PropertyChangedCallback(e)));
public static readonly DependencyProperty ColDefinitionsProperty = ColDefinitionsPropertyKey.DependencyProperty;
/// <summary>
/// 处理「MyGrid.ColDefinitions」属性变更
/// </summary>
protected virtual void ColDefinitions_PropertyChangedCallback(DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is ColDefinitions oldCols) oldCols.CollectionChanged -= Cols_CollectionChanged;
if (e.NewValue is ColDefinitions newCols) newCols.CollectionChanged += Cols_CollectionChanged;
}
private void Cols_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (this.ChildrenContainer != null)
{
this.ChildrenContainer.ColumnDefinitions.Clear();
foreach (var col in this.ColDefinitions)
this.ChildrenContainer.ColumnDefinitions.Add(col);
}
}
}
[DefaultMember("Items")]
public class RowDefinitions : ObservableCollection<RowDefinition> { public RowDefinitions() { } }
[DefaultMember("Items")]
public class ColDefinitions : ObservableCollection<ColumnDefinition> { public ColDefinitions() { } }
}

View File

@@ -95,4 +95,24 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</Style> </Style>
<Style TargetType="{x:Type local:MyGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyGrid}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsControl x:Name="PART_ChildrenContainer" ItemsSource="{TemplateBinding Children}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid x:Name="ChildrenContainer"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace RazorEngineTest
{
public class UICollection : ObservableCollection<UIElement>
{
public UICollection() { }
}
}