【问题标题】:Selecting a Path on Canvas which already has bindings在 Canvas 上选择已绑定的路径
【发布时间】:2014-09-16 15:16:47
【问题描述】:

我对 WPF 还有些陌生,所以如果我遗漏了一两个明显的概念,我深表歉意。

我正在使用遵循 MVVM 范例的 WPF。我真正的问题有点复杂,所以我会尽量简化。假设我在画布上有一些(实际上是很多)路径:

<Canvas x:Name="Canvas1" Width="600" Height="300">
   <Path x:Name="Path1" Style="{StaticResource PathStyle}" .../>
   <Path x:Name="Path2" Style="{StaticResource PathStyle}" .../>
   <Path x:Name="Path3" Style="{StaticResource PathStyle}" .../>
/>

我使用的那种样式包含一些绑定,使所有路径保持相同的颜色,如下所示:

<Style x:Key="PathStyle" TargetType="Path">
    <Setter Property="Fill" Value="{Binding FillColor, Converter={StaticResource BrushConverter}}"/>
    <Setter Property="Stroke" Value="{Binding StrokeColor, Converter={StaticResource BrushConverter}}"/>
</Style>

当用户点击其中一条路径时,我可以捕捉到。当他们这样做时,我想“覆盖”绑定并更改所述路径的颜色,直到用户单击另一个。当他们单击它时,我可以手动设置Path.Stroke,但我不知道在清除SelectedPath 后如何“重新激活”绑定(因此路径保持我设置的任何颜色)。我尝试获取绑定表达式以强制刷新,但返回结果为 null (selectedPath.GetBindingExpression(Path.StrokeProperty))。

TL;DR:我想让它在用户单击路径时变为蓝色。当单击另一条路径时,我希望它根据样式中的绑定返回到 假定 的任何颜色。

有什么想法吗?感谢您抽出宝贵时间!

编辑:感谢您的中肯建议-如果我可以再推一点,如果有嵌套画布怎么办,但我仍然只想选择一个路径?如:

<Canvas x:Name="Canvas_Main" Width="600" Height="300">
   <Canvas x:Name="Canvas1" Width="600" Height="300">
      <Path x:Name="Path1" Style="{StaticResource PathStyle}" .../>
      <Path x:Name="Path2" Style="{StaticResource PathStyle}" .../>
      <Path x:Name="Path3" Style="{StaticResource PathStyle}" .../>
   />
   <Canvas x:Name="Canvas1" Width="600" Height="300">
      <Path x:Name="Path1" Style="{StaticResource PathStyle}" .../>
      <Path x:Name="Path2" Style="{StaticResource PathStyle}" .../>
      <Path x:Name="Path3" Style="{StaticResource PathStyle}" .../>
   />
/>

【问题讨论】:

  • 只是一个美容建议:如果您在自己的风格中使用TargetType="Path",则无需使用x:Key
  • @Natxo 这不仅仅是外观上的改变,它会改变应用样式的元素,所以你应该更加小心。
  • @NovitchiS 你是对的,样式将应用于定义Resources 样式的元素内的所有Path 元素。这就是我认为他想做的事情。
  • 您可能可以使用TreeView 进行嵌套,但是Canvases 不再是其父级Canvases 的直接子级。如果您正在使用画布放置属性,则需要将它们绑定到 ItemContainerStyle,因为项目容器将是画布的直接子项/或者如果您有静态项目,则可以将它们设置在手动创建的 TreeViewItems 上。跨度>

标签: c# wpf xaml canvas mvvm


【解决方案1】:

以下内容:

  1. 使用ListBox,它提供项目选择。
  2. 将路径移动到ListBox 或绑定ListBox.ItemsSource。
  3. 将ListBox.ItemsPanel 设置为Canvas。
  4. 在路径样式中引入在{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}} 上触发的DataTrigger
  5. 将Setter 添加到触发器,以根据需要更改所选路径的颜色。
  6. (如果默认情况下看起来很奇怪,请添加 ListBox.ItemContainerStyle 以覆盖 ListBoxItem.Template。)

编辑:很好奇它是否按预期工作,所以这里有一个独立的例子:

<ListBox Height="300" Width="300">
    <ListBox.Resources>
        <Style TargetType="Path">
            <Setter Property="Stroke" Value="Black"/>
            <Setter Property="Fill" Value="Red"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
                             Value="True">
                    <Setter Property="Fill" Value="Blue"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ListBox.Resources>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <ContentPresenter />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
    <Path>
        <Path.Data>
            <RectangleGeometry Rect="50,50,25,25" />
        </Path.Data>
    </Path>
    <Path>
        <Path.Data>
            <RectangleGeometry Rect="0,0,25,25" />
        </Path.Data>
    </Path>
</ListBox>

【讨论】:

  • 感谢您的回复。我一直在学习 WPF 中的 ListBox 比我以前想象的更强大 :) 如果画布包含嵌套画布怎么办?在这种情况下,TreeView 会在某处发挥作用吗?
  • 一个项目控件只能有一个面板,所以嵌套的Canvases 不起作用。 TreeView 仅包含作为项目控件本身的项目。
【解决方案2】:

此方法也使用 ListBox,但将 ListBoxItem 的 Background 和 BorderBrush 属性用于 Path 的 Fill 和 Stroke:

<ListBox ItemsSource="{Binding PathItems}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="Background" Value="Azure"/>
            <Setter Property="BorderBrush" Value="Black"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Path Data="{Binding Geometry}"
                              Fill="{TemplateBinding Background}"
                              Stroke="{TemplateBinding BorderBrush}"
                              StrokeThickness="3"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Background" Value="Pink"/>
                    <Setter Property="BorderBrush" Value="Red"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

假设存在这样的视图模型:

public class PathItem
{
    public Geometry Geometry { get; set; }
}

public class ViewModel
{
    public ObservableCollection<PathItem> PathItems { get; set; }
}

如果视图模型还应提供选定和未选定状态的填充和描边,您通常会在项目级别定义它们,例如如下所示(这也可以让您选择拥有个人每个项目的颜色或画笔。

public class PathItem
{
    public Geometry Geometry { get; set; }
    public Brush UnselectedFill { get; set; }
    public Brush UnselectedStroke { get; set; }
    public Brush SelectedFill { get; set; }
    public Brush SelectedStroke { get; set; }
}

然后绑定到 ItemContainerStyle 中的那些属性:

<Style TargetType="ListBoxItem">
    <Setter Property="Background" Value="{Binding UnselectedFill}"/>
    <Setter Property="BorderBrush" Value="{Binding UnselectedStroke}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBoxItem">
                <Path Data="{Binding Geometry}"
                      Fill="{TemplateBinding Background}"
                      Stroke="{TemplateBinding BorderBrush}"
                      StrokeThickness="3"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsSelected" Value="True">
            <Setter Property="Background" Value="{Binding SelectedFill}"/>
            <Setter Property="BorderBrush" Value="{Binding SelectedFill}"/>
        </Trigger>
    </Style.Triggers>
</Style>

【讨论】:

  • 直接从ControlTemplate绑定数据,你太脏了:P
  • 在这个问题的范围内,ViewModel实际上只有Fill和Stroke这两种颜色,路径来自于由Expression生成的View。这种情况下的观点是非常具体的。
  • 路径 (UI) 元素在视图中定义,但路径的几何形状(其Data 属性)应由视图模型提供。
  • @H.B.原谅我的天真,为什么不希望在 ControlTemplate 中进行绑定?
  • @AIng: ControlTemplates 应该是“关闭的”,即不了解外部,因为它们应该作为一个独立的可视化表示来独立工作的控件所有上下文。在这里,它在ListBox.ItemContainerStyle 中就地使用,因此或多或少可以接受,但是如果有人尝试将其重构为通用资源,则由于绑定依赖关系,它仅适用于非常特定的DataContexts。这没什么大不了的,但我建议避免它,反正也不难做到。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-16
  • 1970-01-01
  • 2011-07-17
  • 2017-04-26
  • 1970-01-01
  • 2013-10-29
  • 1970-01-01
相关资源
最近更新 更多