【问题标题】:Add multiple XAML based WPF control to Canvas dynamically in VB.Net在 VB.Net 中动态添加多个基于 XAML 的 WPF 控件到 Canvas
【发布时间】:2019-03-02 22:32:59
【问题描述】:

和主题一样,我想动态添加相同模板但不同数据的 WPF 控件。

如图所示,我要复制的控件有些复杂。整个控件被包裹在CanvasScrollViewer 内。 每个StackPanel 包装TextBlockanother Canvas Control,而这个StackPanel 是我想要重现的。

代码如下:

<ScrollViewer x:Name="ScrollBoard" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
            <Canvas x:Name="CanvasBoard" VerticalAlignment="Center" HorizontalAlignment="Center" Width="200" Height="250" Background="Gray">

                <StackPanel x:Name="CanvasStack" Background="DimGray">
                    <CheckBox />

                    <Border x:Name="CanvasBorder" BorderBrush="Black" BorderThickness="1">
                        <Canvas Width="150" Height="200" ClipToBounds="True">

                            <Image x:Name="CanvasImage" Canvas.Left="0" Canvas.Top="0" Stretch="Fill" Source="C:\test.jpg"/>

                        </Canvas>
                    </Border>

                    <TextBlock Text="Test.jpg" />
                </StackPanel>

            </Canvas>
        </ScrollViewer>

我想在 CanvasBoard Canvas 控件中复制 CanvasStack StackPanel 控件。

当然,不只是复制,还想控制它。 比如改变位置,编辑TextBlock文字,替换Image并获取点击事件等等。

另外,我不会使用ListBoxListView,因为每个控件都应该位于具有不同大小的绝对x、y 坐标中。

有一些示例可以执行类似的操作,例如“向某个控件添加按钮”。但是我发现只是在后端添加了带有硬编码属性的控件,这可能不适合这种复杂的控件。

提前谢谢你。

【问题讨论】:

  • 您应该将其创建为用户控件并查看stackoverflow.com/questions/1093429/… 以在 xaml 中使用它
  • @ViníciusGabriel 我应该为此使用自定义用户控件吗?如果可能的话,如果可能的话,我想亲自避免它。但如果那是体面的方式,是的,我必须遵循它。
  • 如果你想避免它,那么试试:stackoverflow.com/a/27188799/10008842。如果你成功了,别忘了加上 x:Shared="False" 和 x:Key="yourKey"。
  • 为什么要避免制作自己的用户控件?控件和类通常提供封装和关注点分离,使您的代码更整洁、更易于理解,尤其是在您的情况下,更易于重用

标签: c# wpf vb.net visual-studio controls


【解决方案1】:

当您想多次创建相同的用户界面时,您应该考虑模板。

当您想要更改任何内容的属性时,您应该考虑数据模板并将这些更改的内容绑定到视图模型的公共属性。

在同一区域重复控件时,第一个候选对象应该是某种项目控件。

其中有一个堆栈面板,可以容纳所有内容 - 但您可以轻松更改它。

对于一个画布上的许多东西,您可以使一个项目的项目面板控制一个画布。

你并不一定需要一个用户控件来封装你的标记,你可以只需要一个数据模板。

为您的窗口构建视图模型。为您想要重现的每一个事物构建一个视图模型 (vm)。

将 vm 的 observablecollection 绑定到 itemscontrol 的 itemssource。

定义与该类型视图模型关联的数据模板。

我在 c# 中工作,所以如果我尝试编写 VB 代码,我可能会出错。通过在线转换器运行以下代码。

标记:

<Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
    <ItemsControl x:Name="ic" ItemsSource="{Binding Items}">
        <ItemsControl.Resources>
            <DataTemplate DataType="{x:Type local:StackyVM}">
                <StackPanel x:Name="CanvasStack" Background="DimGray">
                    <CheckBox />
                    <Border x:Name="CanvasBorder" BorderBrush="Black" BorderThickness="1">
                        <Canvas Width="150" Height="200" ClipToBounds="True">

                            <Image x:Name="CanvasImage" Canvas.Left="0" Canvas.Top="0" Stretch="Fill" 
                                   Source="{Binding ImageSource}"/>
                        </Canvas>
                    </Border>
                    <TextBlock Text="Test.jpg" />
                </StackPanel>
            </DataTemplate>
        </ItemsControl.Resources>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas Name="TheCanvas"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Top" Value="{Binding Top}"/>
                <Setter Property="Canvas.Left" Value="{Binding Left}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>

每个堆栈的视图模型

public class StackyVM : BaseViewModel
{
    private Double left;

    public Double Left
    {
        get { return left; }
        set
        {
            left = value;
            RaisePropertyChanged();
        }
    }

    private Double top;

    public Double Top
    {
        get { return top; }
        set { top = value; RaisePropertyChanged(); }
    }

    public string ImageUrl { get; set; }

}

添加任何其他因堆栈而异的属性并绑定它们。 为每个堆栈实例化其中一个并将其添加到绑定的 observablecollection:

public class MainWindowViewModel : BaseViewModel
{
    public ObservableCollection<StackyVM> Items { get; set; }

然后每个都将被模板化到一个堆栈中。

BaseViewModel 实现 inotifypropertychanged:

public  class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

【讨论】:

    猜你喜欢
    • 2012-03-16
    • 1970-01-01
    • 2023-03-27
    • 2010-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多