如何在 ListBox 项实例之间共享资源?
Posted
技术标签:
【中文标题】如何在 ListBox 项实例之间共享资源?【英文标题】:How can you share a resource between ListBox item instances? 【发布时间】:2012-12-03 22:31:11 【问题描述】:我们有一个自定义渲染的 ListBox,它维护一个基于其宽度的 StreamGeometry 对象的实例。然后,该控件需要将该 StreamGeometry 实例与其所有项目共享以进行渲染。
我们能想到的唯一方法是将 StreamGeometry 实例放在 ListBox 的 ViewModel 中,然后在单独的 DataTemplates 中绑定到它,考虑到这是一个仅限视图的东西,因此不应该在完全是 ViewModel。
注意:我们也可以通过 ListBox 上的附加属性(或 ListBox 的子类)来存储它,但我们仍然需要绑定一个仅查看的东西,这对我来说似乎是错误的。
有什么想法吗?
【问题讨论】:
Window.Resources
呢?
不,因为它的 ListBox 实例是特定的并且可以有很多。另外,它是由 ListBox 的属性决定的。我的实际问题更多是关于通过数据模板对项目本身的实际共享。我的意思是这甚至是正确的方法吗?我认为绑定可能很慢。
资源字典中的一个对象(例如,一个 PathGeometry,如果这就是您所说的 GraphicsPath 的意思)可以由任意数量的 ListBoxItem 共享。
Clemens,正如我所说,它是 ListBox 的每个实例,这意味着每个 ListBox 必须维护自己的实例。问题更多的是从 ListBox(或它的 ViewModel)获取 StreamGeometry 实例到单个项目模板实例的最有效方法。 StreamGeometry 属性的绑定似乎会很慢,但我可能是错的。
那么当每个 ListBoxItem 都有自己的实例时,“共享”到底是什么意思?也许您提供一些示例 XAML?
【参考方案1】:
您可以使 StreamGeometry 成为自定义列表视图上的依赖属性,然后通过 Binding MyGeometry, RelativeSource=RelativeSource AncestorType=ListView
引用它。
这样,不涉及 ViewModel。
Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<!-- default lsitviewitem style except for added path -->
<Style TargetType="x:Type ListViewItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Binding HorizontalContentAlignment, RelativeSource=RelativeSource AncestorType=x:Type ItemsControl"/>
<Setter Property="VerticalContentAlignment" Value="Binding VerticalContentAlignment, RelativeSource=RelativeSource AncestorType=x:Type ItemsControl"/>
<Setter Property="Padding" Value="2,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="x:Type ListViewItem">
<Border x:Name="Bd" BorderBrush="TemplateBinding BorderBrush" BorderThickness="TemplateBinding BorderThickness" Background="TemplateBinding Background" Padding="TemplateBinding Padding" SnapsToDevicePixels="true">
<StackPanel Orientation="Horizontal">
<ContentPresenter HorizontalAlignment="TemplateBinding HorizontalContentAlignment" SnapsToDevicePixels="TemplateBinding SnapsToDevicePixels" VerticalAlignment="TemplateBinding VerticalContentAlignment"/>
<!-- added path-->
<Path Stretch="Uniform" Stroke="DarkBlue" Fill="DarkOrchid" Data="Binding MyGeometry, RelativeSource=RelativeSource AncestorType=ListView" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="DynamicResource x:Static SystemColors.HighlightBrushKey"/>
<Setter Property="Foreground" Value="DynamicResource x:Static SystemColors.HighlightTextBrushKey"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="DynamicResource x:Static SystemColors.InactiveSelectionHighlightBrushKey"/>
<Setter Property="Foreground" Value="DynamicResource x:Static SystemColors.InactiveSelectionHighlightTextBrushKey"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="DynamicResource x:Static SystemColors.GrayTextBrushKey"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid >
<local:CustomListView Margin="20" >
<local:CustomListView.Items>
<ListViewItem Content="ListViewItem1" />
<ListViewItem Content="ListViewItem2" />
<ListViewItem Content="ListViewItem3" />
</local:CustomListView.Items>
</local:CustomListView>
</Grid>
</Window>
自定义列表视图:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfApplication1
public class CustomListView : ListView
public StreamGeometry MyGeometry get return (StreamGeometry)GetValue(MyGeometryProperty); set SetValue(MyGeometryProperty, value);
public static readonly DependencyProperty MyGeometryProperty = DependencyProperty.Register("MyGeometry", typeof(StreamGeometry), typeof(CustomListView), new PropertyMetadata(null));
protected override void OnRender(DrawingContext drawingContext)
StreamGeometry geometry = new StreamGeometry(); // directly opening MyGeometry results in "must have isfrozen set to false to modify" error
using (StreamGeometryContext context = geometry.Open())
Point p1 = new Point(this.ActualWidth * (2d / 5d), 0);
Point p2 = new Point(this.ActualWidth / 2d, -10);
Point p3 = new Point(this.ActualWidth * (3d / 5d), 0);
context.BeginFigure(p1, true, true);
List<Point> points = new List<Point>() p2, p3 ;
context.PolyLineTo(points, true, true);
drawingContext.DrawGeometry(Brushes.DarkOrchid, new Pen(Brushes.DarkBlue, 1), geometry);
this.MyGeometry = geometry;
base.OnRender(drawingContext);
【讨论】:
这与我在问题中提到的相似;唯一的区别是我说的是使用附加属性,而您使用的是子类。此外,我们不想使用这样的绑定,因为我们在使用路径的 ListBoxItems 中进行自定义渲染,而您使用可视化树,我相信*会使 UI 混乱(我们的路径非常复杂。)(我打了星号是因为在幕后,渲染实际上可能是在构建视觉树本身,这意味着我的陈述只是部分正确。我需要在那里进行更多的研究。) 看来我无法获得您的目标。我试图理解:当你说你自定义渲染你的列表框时,这并不需要一个子类,所以你没有覆盖 OnRender 方法?什么是“混乱”的 UI,反应迟钝?我认为我的方法有一个共享路径实例。如果您想自定义呈现它而不是直接将其放入可视化树中,则可以将其绑定到 CustomListViewItem 的属性。至于绑定,我绝对不明白你的担心。这是绑定到另一个视觉对象的 dp,与 Data/ViewModel 无关! 我们正在通过ItemContainerStyle
应用的 ListBoxItem 的子类中进行自定义渲染。然而,我们希望 列表框的单个实例 的每个 ListBoxItem 共享一个路径,因此 ListBox 自然必须保存该路径的实例。我只是想知道是否有一个好的方法可以做到这一点。您的方式将起作用,与附加属性相同。我只是讨厌使用绑定来获取它的想法,更不用说使用RelativeSource,但我不知道ListBoxItem 获取它的任何其他方式。 (话又说回来,我认为它确实对 ItemsControl 有一些参考)
好吧,您可以通过(this.Parent as CustomListView).MyGeometry
访问 CustomListViewItem 中的 StreamGeometry,但在我看来,您最终会尝试以这种方式制造您自己的绑定实现。
实际上,从我发的另一篇文章 (***.com/questions/14118414/…) 中,容器的父级(即 ListBoxItem、ComboBoxItem 等)返回 null,而不是 ItemsControl 本身,所以这也不起作用。我同意它应该是非常简单的(而且根本不会是一种约束力!效率更高!),但 MS 的权力显然不同意。如果你问我就疯了。以上是关于如何在 ListBox 项实例之间共享资源?的主要内容,如果未能解决你的问题,请参考以下文章
如何在同一个 monorepo 中的 Python 项目之间共享开发依赖项?
如何使用私有IP在两个项目之间共享Google Cloud SQL实例?