将 UIElement 从 wpf 中的另一个用户控件添加到画布(位于用户控件中)



我的项目中有一些UserControls,如pnlCanvas和pnlTools

pnlTools中有几个按钮,例如"添加"、"添加矩形"、">添加文本"......

当用户单击其中一个按钮时,会将一个元素添加到位于pnlCanvas 中的 Canvas(cnvsObjects) 的子项中。

我的MainWindow.xaml是这样的:

<Window x:Class=...>
<Grid>
...
<local:pnlCanvas Grid.Column="2"/>
<GridSplitter Grid.Column="3" HorizontalAlignment="Stretch"/>
<local:pnlTools Grid.Column="4" />
...
</Grid>
</Window>

pnlCanvas.xaml

<UserControl x:Class=...>
<GroupBox>
<GroupBox.Header...>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Canvas x:Name="cnvsObjects" Width="1920" Height=...>
</Canvas>
</ScrollViewer>
</GroupBox>
</UserControl>

pnlTools.xaml

<UserControl x:Class=...>
<GroupBox>
<GroupBox.Header...>
<StackPanel>
<Button Content="Add Text" Click="Button_Click"></Button>
<Button Content="Add Rectangle"></Button>
<Button Content="Add Line"></Button>
...
</StackPanel>
</GroupBox>
</UserControl>

pnlTools.xaml.cs

....
public partial class pnlTools : UserControl
{
public pnlTools()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
TextBlock tb = new TextBlock();
tb.TextWrapping = TextWrapping.Wrap;
tb.Margin = new Thickness(10);
tb.Text = "A Text as Test";
cnvsObjects.Children.Add(tb); // Error!
}
}
}

正如我所搜索的,我知道在这种情况下我应该使用类似依赖属性的东西。如果它是一个 TextBlock,我可以使用数据绑定和依赖属性。但它不是一个属性,而是一个方法(Children.Add)。

我是WPF的新手,所以如果所有东西都在MainWindow.Xaml中,我没有问题。我已经将MainWindos.xaml划分为一些UserControls(嵌套),以降低复杂性并避免文件变得巨大。我选择用户控制是为了这个目的吗?还是我应该使用其他东西?最好的方法是什么?

抱歉,这篇文章太长了。我无法分析与这个问题相关的其他问题和答案,因为它们对我来说太复杂了。谢谢。

您在错误的上下文中执行Button.Click事件处理程序。如果有意义的话,则必须在有权访问pnlCanvas控件的上下文中处理Button.Click事件。根据您发布的示例,正确的上下文是MainWindow,因为它同时托管pnlCanvaspnlTools

建议的解决方案是更改整体设计。这将涉及引入数据模型类,这些类表示/保存您希望添加到 Canvas 的UIElement的数据.
此类数据将包括画布上的位置(x/y 坐标)和一些要显示的数据,例如 text.
您可以将这些数据模型添加到用作ListBox绑定源的集合中。Canvas本身就是ListBox的面板,通过将其分配给ListBox.ItemsPanel属性。然后,为每个数据模型类型定义一个DataTemplate,其中DataTemplate包含要显示的实际控件。请参阅 Microsoft 文档:数据模板化概述以了解DataTemplate

但是,若要修复示例,必须首先让PnlCanvas控件公开一个公共方法,该方法允许外部控件(如MainWindow)向其内部Canvas添加元素(请注意,C# 中类的正确命名将使用PascalCasing)。 例如,pnlTools应该是PnlTools。请参阅Microsoft文档:命名准则。所有提供的代码示例都将使用官方的 C# 命名约定):

public void AddText(string text, Point position)
{
var textBlock = new TextBlock() { Text = text };
// Position the element on the Canvas
Canvas.SetLeft(textBlock, position.X);
Canvas.SetTop(textBlock, position.Y);
this.cnvsObjects.Children.Add(textBlock);
}

接下来,PnlCanvas公开一组路由命令,PnlTools控件中的按钮可以使用这些命令。然后,MainWindow将侦听这些命令。请参阅Microsoft文档:命令概述

然后,完整的PnlCanvas类将如下所示(示例仅显示向Canvas或任何其他Panel添加文本):

PnlCanvas.xaml.cs

public partial class PnlCanvas : UserControl
{
// TODO::Add commands to support other content
public static RoutedCommand AddTextCommand { get; } = new RoutedCommand("AddTextCommand", typeof(PnlCanvas));
public PnlCanvas()
{
InitializeComponent();
}
// TODO::Add methods to support other content
public void AddText(string text, Point position)
{
var textBlock = new TextBlock() { Text = text };
// Position the element on the Canvas
Canvas.SetLeft(textBlock, position.X);
Canvas.SetTop(textBlock, position.Y);
this.cnvsObjects.Children.Add(textBlock);
}
}

然后PnlTools使用PnlCanvas中定义的路由命令:

PnlTools.xaml

<UserControl>
<StackPanel>
<Button Content="Add Text" 
Command="{x:Static local:PnlCanvas.AddTextCommand}" />
</StackPanel>
</UserControl>

最后,让我们MainWindow执行命令:

MainWindow.xaml.cs

partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var addTextCommandBinding = new CommandBinding(PnlCanvas.AddTextCommand, ExecuteAddTextCommand, CanExecuteAddTextCommand);
this.CommandBindings.Add(addCircleCommandBinding);
}
private void CanExecuteAddTextCommand(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void ExecuteAddTextCommand(object sender, ExecutedRoutedEventArgs e)
{
var textPosition = new Point(100, 100);
this.ElementHost.AddText("Some added text", textPosition);
}
}

使用UserControl背后的一般思路是两件事:

从长远来看,使
  1. 代码更具可读性、更有条理,更易于维护和扩展。
  2. 避免冗余代码和重复的代码块。当应用程序在不同的窗口或页面中使用类似的 UI 模式时,最好设计一个UserControl并在需要的任何位置使用它。

回到主要问题,可以在 PnlTools 代码隐藏中定义委托/事件,并在 MainWindow 代码隐藏中为其注册事件侦听器。

PnlTools.xaml.cs

public partial class PnlTools : UserControl
{
public delegate void OnClickEventHandler();
public event OnClickEventHandler OnClickEvent;
public PnlTools()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
OnClickEvent();
}
}

MainWindow.xaml

<Window x:Class=...>
<Grid>
...
<local:PnlCanvas Grid.Column="2"/>
<GridSplitter Grid.Column="3" HorizontalAlignment="Stretch"/>
<local:PnlTools x:name="myPnlCanvas" Grid.Column="4" />
...
</Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
pnlTools.OnClickEvent += PnlTools_OnClickEvent;
}
private void PnlTools_OnClickEvent()
{
TextBlock tb = new TextBlock();
tb.TextWrapping = TextWrapping.Wrap;
tb.Margin = new Thickness(10);
tb.Text = "A Text as Test";
myPnlCanvas.cnvsObjects.Children.Add(tb);
}
}

相关内容

  • 没有找到相关文章

最新更新