c# - 在 VB.Net 中动态添加多个基于 XAML 的 WPF 控件到 Canvas
问题描述
与主题一样,我想动态添加相同模板但不同数据的 WPF 控件。
正如您在图片中看到的,我要复制的控件有些复杂。整体控制被包裹在Canvas
里面ScrollViewer
。每个StackPanel
包装TextBlock
和another
画布控制,这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 Control 中复制CanvasStack控件 。StackPanel
Canvas
当然,不只是重复,还想控制它。比如改变位置、编辑TextBlock
文本、替换Image
和获取点击事件等等。
另外,我不会使用它ListBox
,ListView
因为每个控件都应该位于绝对 x,y 坐标中,具有不同的大小。
有一些示例执行类似的操作,例如“将按钮添加到某些控件”。但是我发现只是在后端添加了具有硬编码属性的控件,这可能不适合这种复杂的控件。
先感谢您。
解决方案
每当您考虑多次创建相同的用户界面时,您应该考虑模板。
当您想要更改任何内容的属性时,您应该考虑数据模板并将这些更改的内容绑定到视图模型的公共属性。
在同一区域中重复控件时,第一个候选对象应该是某种项目控件。
里面有一个堆栈面板,里面有所有东西——但你可以很容易地改变它。
对于一个画布上的许多东西,您可以使项目的项目面板控制一个画布。
您并不绝对需要一个用户控件来封装您的标记,您可以只拥有一个数据模板。
为您的窗口构建视图模型。为您想要重现的每一件事(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));
}
}
推荐阅读
- node-red - Node-RED 在输入字段中使用多个环境变量
- docker - 将此图像 uvicorn-gunicorn-starlette-docker 用于生产是否安全?
- java - 水平滚动屏幕上的Appium滚动问题
- ios - CoreImageContext 的 CreateCGImage 产生错误的 CGRect
- python - 如何处理 JSON 中的缺失数据
- batch-file - cmd批处理 - usebackq in for:要拆分的字符串从逗号分隔更改为空格分隔
- redis - Redis-cli 未连接到 redis-server
- google-chrome-devtools - 如何远程调试已在 Android 上“添加到主屏幕”的 PWA?
- android - PopupWindow 与 Navigation Drawer 重叠
- database - IIS 如何从 LDAP 请求信息?