如何在 SilverLight 中组合一些 UserControl?

发布于 2024-12-06 15:04:25 字数 1921 浏览 0 评论 0原文

也许这是一个简单的问题,但我找不到答案。 我有三个用户控件,仅颜色不同。其中有代码之一:

<UserControl x:Class="SilverlightApplication14.NodePicture"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SilverlightApplication14">

    <UserControl.Resources>
        <local:NodeViewModel x:Key="Children"  />
    </UserControl.Resources>
    <Grid x:Name="LayoutRootNodePicture" Height="100" Width="100"
          HorizontalAlignment="Center"  DataContext="{Binding Source={StaticResource Children}, Path=Children}" >
        <Canvas x:Name="ParentCanvas" Background="White" Width="100" Height="100" >
            <Rectangle Fill="Yellow" Stroke="Blue" Width="100" Height="100"   >
                </Rectangle  >

        </Canvas>
        <Image HorizontalAlignment="Center"
                       Source="add.png"
                       Stretch="Fill"
                       Width="16"
                       VerticalAlignment="Top"
                       Margin="0,0,2,2"
                       Height="16" MouseLeftButtonDown="Image_MouseLeftButtonDown">
        </Image>
            </Grid>
</UserControl>

如何将它们组合到 ObservableCollection Children 中?

public class NodeViewModel : INotifyPropertyChanged
    {

public ObservableCollection<NodeViewModel> Children
        {
            get { return _children; }
            set
            {
                _children = value;
                NotifyChange("Children");
            }
        }

        private void NotifyChange(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
}

我如何使用此控件集合的元素?

是否有一种简单(或正确的方法)的方法来做到这一点?

Maybe it is a simple question, but I can’t find the answer.
I have three User controls that are different only with colour. There is code one of them:

<UserControl x:Class="SilverlightApplication14.NodePicture"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SilverlightApplication14">

    <UserControl.Resources>
        <local:NodeViewModel x:Key="Children"  />
    </UserControl.Resources>
    <Grid x:Name="LayoutRootNodePicture" Height="100" Width="100"
          HorizontalAlignment="Center"  DataContext="{Binding Source={StaticResource Children}, Path=Children}" >
        <Canvas x:Name="ParentCanvas" Background="White" Width="100" Height="100" >
            <Rectangle Fill="Yellow" Stroke="Blue" Width="100" Height="100"   >
                </Rectangle  >

        </Canvas>
        <Image HorizontalAlignment="Center"
                       Source="add.png"
                       Stretch="Fill"
                       Width="16"
                       VerticalAlignment="Top"
                       Margin="0,0,2,2"
                       Height="16" MouseLeftButtonDown="Image_MouseLeftButtonDown">
        </Image>
            </Grid>
</UserControl>

How can I combine them into ObservableCollection Children?

public class NodeViewModel : INotifyPropertyChanged
    {

public ObservableCollection<NodeViewModel> Children
        {
            get { return _children; }
            set
            {
                _children = value;
                NotifyChange("Children");
            }
        }

        private void NotifyChange(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
}

And how can I use then elements of this controls collection?

Is there a simple (or a right way ) way of doing this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

☆獨立☆ 2024-12-13 15:04:25

据我的理解,您有 3 个用户控件,其名称类似于 NodePicture、GreenNodePicture 和 BlueNodePicture。
首先,如果 3 个控件的差异很小,那么最好只有一个控件使用某些属性值来切换颜色。

假设您的控件因画布上矩形的背景颜色而异。因此,我将更改您的控件:

<Grid x:Name="LayoutRootNodePicture" Height="100" Width="100"
      HorizontalAlignment="Center">
    <Canvas x:Name="ParentCanvas" Background="{Binding NodeColor}" Width="100" Height="100" >
    </Canvas>
    <Image HorizontalAlignment="Center"
                   Source="add.png"
                   Stretch="Fill"
                   Width="16"
                   VerticalAlignment="Top"
                   Margin="0,0,2,2"
                   Height="16" MouseLeftButtonDown="Image_MouseLeftButtonDown">
    </Image>
</Grid>

我删除了 Resources 部分,因为视图不应该创建新的视图模型对象,它应该使用现有的 DataContext。您可以看到矩形的背景颜色基于视图模型的属性NodeColor
让我们将此属性添加到视图模型中:

public class NodeViewModel : INotifyPropertyChanged
{
    private SolidColorBrush _nodeColor;

    public SolidColorBrush NodeColor
    {
        get { return _nodeColor; }
        set
        {
            _nodeColor = value;
            NotifyChange("NodeColor");
        }
    }
    //...

现在,如果您想显示 3 个具有不同颜色的控件,您应该创建 3 个具有不同属性的视图模型。以下是红色、蓝色和绿色视图模型的示例:

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
        var redBrush = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0));
        var greenBrush = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0));
        var blueBrush = new SolidColorBrush(Color.FromArgb(255, 0, 0, 255));

        this.DataContext = new MainViewModel
        {
            Nodes = new ObservableCollection<NodeViewModel>{
                new NodeViewModel 
                { 
                    NodeColor = redBrush,
                    Children = new ObservableCollection<NodeViewModel>{
                        new NodeViewModel { NodeColor = greenBrush, LeftOffset = 65, TopOffset = 10},
                        new NodeViewModel { NodeColor = greenBrush, LeftOffset = 55, TopOffset = 60}
                    }
                }, //red
                new NodeViewModel { NodeColor = greenBrush}, //green
                new NodeViewModel { NodeColor = blueBrush} //blue
            }
        };
    }
}

public class MainViewModel
{
    public ObservableCollection<NodeViewModel> Nodes { get; set; }
}

使用数据模板将视图模型转换为视图:

<ListBox ItemsSource="{Binding Nodes}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <local:NodePicture DataContext="{Binding}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

我没有使用 Children 属性,因为我不知道在哪里使用它。也许子节点显示在画布上。无论如何,如果它很重要 - 您可以提供其他信息,我会提供帮助。

更新:

在画布上绘制子项的最简单方法是添加依赖属性,该属性会在集合更新时更新画布:

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

    public IEnumerable<NodeViewModel> ChildViewModels
    {
        get { return (IEnumerable<NodeViewModel>)GetValue(ChildViewModelsProperty); }
        set { SetValue(ChildViewModelsProperty, value); }
    }

    public static readonly DependencyProperty ChildViewModelsProperty =
        DependencyProperty.Register("ChildViewModels", typeof(IEnumerable<NodeViewModel>), typeof(NodePicture),
        new PropertyMetadata(null, (s, e) => ((NodePicture)s).UpdateCanvas()));

    private void UpdateCanvas()
    {
        this.ParentCanvas.Children.Clear();
        var items = this.ChildViewModels;
        if(items == null)
            return;

        var controls = items.Select(item=>
            {
                var e = new Ellipse{Width = 20, Height = 20};
                e.Fill = item.NodeColor;
                //Or using the data binding
                //BindingOperations.SetBinding(e, Ellipse.FillProperty, new Binding("NodeColor") { Source = item });
                Canvas.SetLeft(e, item.LeftOffset);
                Canvas.SetTop(e, item.TopOffset);
                return e;
            });

        foreach(var c in controls)
            this.ParentCanvas.Children.Add(c);
    }

其中 TopOffset 和 LeftOffset 是 NodeViewModel 类的属性。
之后,您应该在 xaml 代码中设置此属性:

    <DataTemplate>
        <local:NodePicture DataContext="{Binding}" ChildViewModels="{Binding Children}" />
    </DataTemplate>

它不适用于 ObservableColelction 类,因为我没有处理 CollectionChanged 事件。
另一种方法 - 将 ListBox 控件与自定义 ItemsPanelTemplateListBoxItem ControlTemplate 结合使用。但这是更为复杂的解决方案。

As far as I understood you right, you have 3 user controls which have names something like NodePicture, GreenNodePicture and BlueNodePicture.
First of all, if the 3 controls differ to a very little degree, it would be better to have only one control which switches the color using some property value.

Let's suppose that your controls differ by the background color of the rectangle on the canvas. So I would change your control so:

<Grid x:Name="LayoutRootNodePicture" Height="100" Width="100"
      HorizontalAlignment="Center">
    <Canvas x:Name="ParentCanvas" Background="{Binding NodeColor}" Width="100" Height="100" >
    </Canvas>
    <Image HorizontalAlignment="Center"
                   Source="add.png"
                   Stretch="Fill"
                   Width="16"
                   VerticalAlignment="Top"
                   Margin="0,0,2,2"
                   Height="16" MouseLeftButtonDown="Image_MouseLeftButtonDown">
    </Image>
</Grid>

I've removed the Resources section because the view shouldn't create new view model objects, it should use an existing DataContext. You can see that the background color of the rectangle is based on the property NodeColor of the view model.
Let's add this property to the view model:

public class NodeViewModel : INotifyPropertyChanged
{
    private SolidColorBrush _nodeColor;

    public SolidColorBrush NodeColor
    {
        get { return _nodeColor; }
        set
        {
            _nodeColor = value;
            NotifyChange("NodeColor");
        }
    }
    //...

And now if you want to display 3 controls with different color you should create 3 view models with different properties. Here is the example of the red, blue and green viewmodels:

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
        var redBrush = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0));
        var greenBrush = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0));
        var blueBrush = new SolidColorBrush(Color.FromArgb(255, 0, 0, 255));

        this.DataContext = new MainViewModel
        {
            Nodes = new ObservableCollection<NodeViewModel>{
                new NodeViewModel 
                { 
                    NodeColor = redBrush,
                    Children = new ObservableCollection<NodeViewModel>{
                        new NodeViewModel { NodeColor = greenBrush, LeftOffset = 65, TopOffset = 10},
                        new NodeViewModel { NodeColor = greenBrush, LeftOffset = 55, TopOffset = 60}
                    }
                }, //red
                new NodeViewModel { NodeColor = greenBrush}, //green
                new NodeViewModel { NodeColor = blueBrush} //blue
            }
        };
    }
}

public class MainViewModel
{
    public ObservableCollection<NodeViewModel> Nodes { get; set; }
}

View models are translated into the views using data templates:

<ListBox ItemsSource="{Binding Nodes}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <local:NodePicture DataContext="{Binding}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

I haven't used the Children property because I haven't understood where to use it. Maybe child nodes are displayed on the canvas. Anyway if it is important - you can provide additional information and I'll help with this.

Update:

The easiest way to draw child items on the canvas is to add the dependency property which updates the canvas when the collection is updated:

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

    public IEnumerable<NodeViewModel> ChildViewModels
    {
        get { return (IEnumerable<NodeViewModel>)GetValue(ChildViewModelsProperty); }
        set { SetValue(ChildViewModelsProperty, value); }
    }

    public static readonly DependencyProperty ChildViewModelsProperty =
        DependencyProperty.Register("ChildViewModels", typeof(IEnumerable<NodeViewModel>), typeof(NodePicture),
        new PropertyMetadata(null, (s, e) => ((NodePicture)s).UpdateCanvas()));

    private void UpdateCanvas()
    {
        this.ParentCanvas.Children.Clear();
        var items = this.ChildViewModels;
        if(items == null)
            return;

        var controls = items.Select(item=>
            {
                var e = new Ellipse{Width = 20, Height = 20};
                e.Fill = item.NodeColor;
                //Or using the data binding
                //BindingOperations.SetBinding(e, Ellipse.FillProperty, new Binding("NodeColor") { Source = item });
                Canvas.SetLeft(e, item.LeftOffset);
                Canvas.SetTop(e, item.TopOffset);
                return e;
            });

        foreach(var c in controls)
            this.ParentCanvas.Children.Add(c);
    }

Where the TopOffset and LeftOffset are the properties of the NodeViewModel class.
After that you should set this property in the xaml code:

    <DataTemplate>
        <local:NodePicture DataContext="{Binding}" ChildViewModels="{Binding Children}" />
    </DataTemplate>

It won't work with the ObservableColelction class because i didn't handle the CollectionChanged event.
Another approach - to use the ListBox control with the custom ItemsPanelTemplate and ListBoxItem ControlTemplate. But it is the much more complex solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文