XAML 将UIElement从wpf中的另一个UserControl添加到画布(位于UserControl中)

fhg3lkii  于 2022-12-07  发布在  其他
关注(0)|答案(2)|浏览(179)

我的项目中有一些用户控件,如pnlCanvaspnlTools

    • 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>

如果您的计算机上有一个

<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>

如果您的计算机上有一个

....
    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!
        }
    }
}

正如我所搜索的,我知道在这种情况下我应该使用像Dependency Properties这样的东西。如果它是一个TextBlock,我可以使用Data Binding和Dependency Property。但它不是一个Property,而是一个Method(Children. Add)。
我是WPF的新手,所以如果所有的东西都在 * MainWindow. xaml * 中,我就没有问题了。我已经将 * MainWindos. xaml * 分成了一些UserControl(嵌套)来降低复杂性并避免文件变得巨大。我选择UserControl是为了这个目的吗?还是我应该使用其他东西?这样做的最好方法是什么?
很抱歉,这篇文章太长了。我不能分析与这个问题相关的其他问题和答案,因为它们对我来说太复杂了。谢谢。

gudnpqoy

gudnpqoy1#

You are executing the Button.Click event handlers in the wrong context. You would have to handle the Button.Click event in a context that has access to the pnlCanvas control, if this makes sense. Based on your posted example, the correct context would be the MainWindow as it hosts both pnlCanvas and pnlTools .
The recommended solution would be to change your overall design. This would involve the introduction of data model classes, that represent/hold the data for a UIElement , that you wish to add to the Canvas.
Such data would include the position on the canvas (x/y coordinates) and some data to display e.g., a text.
You would add those data models to a collection that serves as a binding source for a ListBox . The Canvas itself would be the panel of the ListBox , by assigning it to the ListBox.ItemsPanel property. You then define a DataTemplate for each data model type, where the DataTemplate contains the actual control you want to show. See Microsoft Docs: Data Templating Overview to learn about DataTemplate .
However, to fix your example you must first let the PnlCanvas control expose a public method that allows external controlslike the MainWindow to add elements to its internal Canvas (note that the proper naming for classes in C# would use PascalCasing. For example pnlTools should be PnlTools . See Microsoft Docs: Naming Guidelines. All provided code examples will use the official C# naming convention):

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);
}

Next, let PnlCanvas expose a set of routed commands, that the buttons in the PnlTools control can use. The MainWindow will then listen to those commands. See Microsoft Docs: Commanding Overview
The complete PnlCanvas class will then look as follows (example only shows adding text to the Canvas or any other 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);
  }
}

Then let PnlTools use the routed commands defined in PnlCanvas :

PnlTools.xaml

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

Finally, let MainWindow execute the command:

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);
  }
}
pxy2qtax

pxy2qtax2#

使用UserControl的基本思想有两点:
1.保持代码更易读、更有组织、更易于维护和长期扩展。
1.避免冗余代码和重复的代码块。当应用程序在不同的窗口或页面中使用相似的UI模式时,设计一个UserControl并在需要的任何地方使用它是一个很好的实践。
回到您的主要问题,您可以在PnlTools代码隐藏中定义一个委托/事件,并在MainWindow代码隐藏中向其注册一个事件侦听器。

    • 产品工具. 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();
    }
}
    • 主窗口. 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>
    • 主窗口. 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);
    }
}

相关问题