【问题标题】:How to access a Canvas inside a WPF UserControl?如何访问 WPF 用户控件中的画布?
【发布时间】:2017-07-21 07:43:08
【问题描述】:

我是使用 WPF 的新手,但非常喜欢在 xaml 中编写视图代码并在视图模型中支持代码的想法。我想做的是通过将 Canvas 与状态栏相关联来扩展 Canvas 的使用,该状态栏根据 Canvas 的内容和鼠标位置显示状态文本(下面的程式化代码不包括此)。

按照https://www.codeproject.com/Articles/82464/How-to-Embed-Arbitrary-Content-in-a-WPF-Control,我的方法是创建一个包含 Canvas 的 UserControl 并在其中放置一个 ContentPresenter。

我有两个问题要解决: 1) 我需要做什么才能允许多个子控件,就像 Canvas 允许多个子控件一样? 2) 如何从主窗口代码中访问 Canvas.Left 等 Canvas 的属性?

提前感谢您提出的任何建议。

UserControl xaml 代码、UserControl 后面代码、主窗口 xaml 代码:

<UserControl x:Class="SO.CanvasUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:SO"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
  <UserControl.Template>
    <ControlTemplate TargetType="{x:Type local:CanvasUserControl}">
      <Canvas Width="200" Height="100" Background="Green">
        <ContentPresenter/>
      </Canvas>
    </ControlTemplate>
  </UserControl.Template>
</UserControl>

后面的代码:

  public partial class CanvasUserControl : UserControl
  {
    public CanvasUserControl()
    {
      InitializeComponent();
    }
  }

主窗口:

<Window x:Class="SO.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SO"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>

    <!--   works as expected
      <Canvas Width="200" Height="100" Background="Green">
      <Line X1="0" Y1="0" X2="200" Y2="100" Stroke="Red"/>
      <Line X1="200" Y1="0" X2="0" Y2="100" Stroke="Red"/>
    </Canvas>
  -->

    <!-- works as expected
     <Canvas Width="200" Height="100" Background="Green" x:Name="MyCanvas">
      <Line X1="{Binding ElementName=MyCanvas, Path=Left}" Y1="{Binding ElementName=MyCanvas, Path=Top}" X2="{Binding ElementName=MyCanvas, Path=ActualWidth}" Y2="{Binding ElementName=MyCanvas, Path=ActualHeight}" Stroke="Red"/>
      <Line X1="{Binding ElementName=MyCanvas, Path=ActualWidth}" Y1="{Binding ElementName=MyCanvas, Path=Top}" X2="{Binding ElementName=MyCanvas, Path=Left}" Y2="{Binding ElementName=MyCanvas, Path=ActualHeight}" Stroke="Red"/>
    </Canvas>
     -->


    <!-- How do I add more than one child control as nested content for the Canvas?
    <local:CanvasUserControl x:Name="MyCanvasUserControl">
      <Line X1="0" Y1="0" X2="200" Y2="100" Stroke="Red"/>
      <Line X1="200" Y1="0" X2="0" Y2="100" Stroke="Green"/>
    </local:CanvasUserControl>
    -->

    <!-- How do I access dependency properties of the Canvas?
    <local:CanvasUserControl x:Name="MyCanvasUserControl">
      <Line X1="{Binding ElementName=MyCanvasUserControl, Path=Left}" Y1="{Binding ElementName=MyCanvasUserControl, Path=Top}" X2="{Binding ElementName=MyCanvasUserControl, Path=ActualWidth}" Y2="{Binding ElementName=MyCanvasUserControl, Path=ActualHeight}" Stroke="Red"/>
    </local:CanvasUserControl>
    -->

  </Grid>
</Window>

【问题讨论】:

  • A UserControl 继承自 ContentControl。它有一个Content 属性,类型为Object。我不确定您在这里真正想要的是UserControl。您是否只是希望能够将 Canvas 的一堆属性汇总到某种包中,然后一起重用它们?这可以很容易地完成,但我会用 Style 来代替。
  • 我想做的是创建一个可重复使用的控件,它的作用类似于 Canvas,但它下面还有一个 StatusBar。我打算在其中使用它的每个项目都会在其“Canvas Children”列表中显示它想要的任何内容,例如两条对角线。事实上,我已经让所有这些代码工作了,但是厌倦了复制粘贴 20-30 行的 xaml 以及消耗鼠标位置并返回状态栏文本的 ViewModel 代码。从我对 StackOverflow 以及其他网站的背景阅读来看,UserControl 似乎是要走的路。
  • 定义“随心所欲”。
  • 如果你只想在没有任何额外 XAML 噪音的情况下加入随机的东西,你需要一个 ItemsControl 的子类,它有一个 Canvas 和一个 ItemsPanel。如果要为其添加状态栏,则需要将控件的Template 替换为添加状态栏的内容。到目前为止,所有这些都可以在 Style 中完成,甚至无需编写自己的子类。编写自己的子类的唯一原因是向消费者公开Canvas 的属性(例如,添加Line 对象的MainWIndow 中的XAML)。你必须给你的子类一个依赖...
  • 我认为 ItemsControl 是要走的路。不幸的是,我向你扔了一堆关于模板和项目面板的伊特鲁里亚象形文字,现在我必须在几分钟内回家。 Here's an answer that's very close to your case,不过; Template 将用于显示您的控件。

标签: wpf


【解决方案1】:

也许我没有完全理解您的问题,但听起来您只想拥有一个内部有预定义状态栏的 Canvas。您可以通过扩展 Canvas 而不是 UserControl 来轻松做到这一点。这是一个扩展 Canvas 并具有状态栏的自定义组件。

<Canvas x:Class="SO.CanvasUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:SO"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Border Width="525" Height="30" Background="Black" Canvas.Bottom="0">
        <TextBlock Foreground="White" Text="Hello world" FontSize="16" />
    </Border>
</Canvas>

现在将它添加到您的主窗口并分配您喜欢的任何孩子。您将看到状态栏与所有子项一起显示。由于该组件扩展了 Canvas,因此您可以添加任意数量的子组件,并且可以绑定到 Canvas 依赖属性。

<Window x:Class="SO.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SO"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <local:CanvasUserControl x:Name="MyCanvasUserControl" >
        <Line X1="0" Y1="0" X2="200" Y2="100" Stroke="Red"/>
        <Line X1="200" Y1="0" X2="0" Y2="100" Stroke="Green"/>
        <Line X1="{Binding ElementName=MyCanvasUserControl, Path=Left}" Y1="{Binding ElementName=MyCanvasUserControl, Path=Top}" X2="{Binding ElementName=MyCanvasUserControl, Path=ActualWidth}" Y2="{Binding ElementName=MyCanvasUserControl, Path=ActualHeight}" Stroke="Red"/>
    </local:CanvasUserControl>
</Window>

【讨论】:

    【解决方案2】:

    只是想补充一下,上面https://stackoverflow.com/a/42558704/9916025 答案中的 MouseMove 问题是由于未为 Canvas 设置背景颜色。

    所以要解决 MouseMove 的问题,你应该添加

    
    <ItemsPanelTemplate>
        <local:CustomCanvas Background="Transparent"
                    />
    </ItemsPanelTemplate>
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多