【发布时间】:2021-06-24 05:38:26
【问题描述】:
我正在创建一个具有自定义控件的 UWP 应用程序。
我有一个看起来像这样的模型:
public enum SpanType {
Normal,
Suspend,
Resume,
}
public struct Span {
public SpanType Type;
public int StackIndex;
public string Category;
public string Name;
public UInt64 ThreadId;
public UInt64 SpanId;
public UInt64 StartTime;
public UInt64 Duration;
}
public struct ThreadSpans {
public UInt64 ThreadId;
public List<Span> Spans;
public int RowCount;
}
还有自定义控件:
public sealed partial class ThreadSpanControl : UserControl {
public ThreadSpanControl() {
DataContext = this;
this.InitializeComponent();
}
public List<ThreadSpans> Threads {
get { return (List<ThreadSpans>)GetValue(ThreadsProperty); }
set { SetValue(ThreadsProperty, value); }
}
public static readonly DependencyProperty ThreadsProperty =
DependencyProperty.Register("Threads", typeof(List<ThreadSpans>), typeof(ThreadSpanControl), null);
public float RowHeight {
get { return (float)GetValue(RowHeightProperty); }
set { SetValue(RowHeightProperty, value); }
}
public static readonly DependencyProperty RowHeightProperty =
DependencyProperty.Register("RowHeight", typeof(float), typeof(ThreadSpanControl), new PropertyMetadata(20.0f));
}
我的主窗口是这样使用它的:
<controls:ThreadSpanControl Threads="{x:Bind ViewModel.Threads,Mode=OneWay}" />
我想做的是能够在我的自定义控件的 ItemsControl 中使用 RowHeight 属性。比如:
<UserControl
x:Class="Fibofiler.Shared.Controls.ThreadSpanControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Fibofiler.Shared.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Fibofiler.Shared.Controls"
xmlns:models="using:Fibofiler.Shared.Models"
xmlns:skia="using:SkiaSharp.Views.UWP"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0" ItemsSource="{x:Bind Threads,Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="models:ThreadSpans">
<TextBlock Text="{x:Bind ThreadId,Mode=OneWay}" Height="{x:Bind controls:Converters.CalcHeight(RowCount, this.RowHeight)}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<skia:SKXamlCanvas Grid.Column="1" PaintSurface="SKXamlCanvas_OnPaintSurface" />
</Grid>
</UserControl>
但是,这是不可能的。 ItemsControl 模板的范围仅是项目本身。据我所知,没有办法访问父母的属性。这是正确的吗?
所以,为了解决这个问题,我想也许我可以创建一个名为RowHeaders 的新依赖属性,它直接计算我需要的东西。因此,我可以将ItemsSource 绑定到此,并在模板中包含我需要的所有信息。
public struct ThreadHeader {
public UInt64 ThreadId;
public double Height;
};
public sealed partial class ThreadSpanControl : UserControl {
...
public List<ThreadHeader> ThreadHeaders {
get { return (List<ThreadHeader>)GetValue(ThreadHeadersProperty); }
}
private static readonly DependencyProperty ThreadHeadersProperty =
DependencyProperty.Register("ThreadHeaders", typeof(List<ThreadHeader>), typeof(ThreadSpanControl), null);
...
}
问题在于这个应该是一个“计算/派生”的属性。也就是说,它直接依赖于Threads / RowHeight 属性。而且我不确定如何使用 DependencyProperties 来做到这一点。网上所有的资料都说“普通”的Property wrapper不能有任何自定义逻辑,因为在运行时,代码甚至都不会被使用。
我如何拥有一个“计算/派生”的 DependencyProperty,其值是根据其他属性计算得出的?
我想到的另一个选项是实现 INotifyPropertyChanged,并使用它。但是,我搜索的所有地方都说,“永远不要在控件中使用 INPC。总是使用 use DependencyProperties。它们更有效”。可以这么说,但据我所知,DependencyProperties 不太灵活。它们只能是简单的 get/set,对他人的依赖为零。
想法?我只是走错路了吗?从而遇到问题?或者我缺少的自定义控件/DependencyProperties 是否有更多功能?
附加上下文
我的自定义上下文是渲染一吨(20000 到 200000)代表时间跨度的矩形。我最初是用嵌套的 ItemControls 和 Rectangles 做的,但由于每个矩形的 Control 开销很大,所以性能很糟糕。 因此,相反,我使用 SkiaSharp 画布为我呈现所有矩形。加载后数据都是静态的,因此每个矩形的特征损失都很好。
我在这里要解决的问题是如何将标签与相应的行对齐。在上图中,您可以看到它们只是一个接一个。从数据中,我知道每个线程的最大行数,并以像素为单位定义行高。我只是不知道为标签设置每个 TextBlock 的最佳方法。
更新 1
按照@Knoop 的建议,我尝试为自定义控件命名,这样我就可以从模板中访问它,但这不起作用。
<UserControl
x:Class="Fibofiler.Shared.Controls.ThreadSpanControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Fibofiler.Shared.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Fibofiler.Shared.Controls"
xmlns:models="using:Fibofiler.Shared.Models"
xmlns:skia="using:SkiaSharp.Views.UWP"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
x:Name="thisControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0" ItemsSource="{x:Bind Threads,Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="models:ThreadSpans">
<TextBlock Text="{x:Bind ThreadId,Mode=OneWay}" Height="{x:Bind controls:Converters.CalculateHeight(RowCount, thisControl.RowHeight)}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<skia:SKXamlCanvas Grid.Column="1" PaintSurface="SKXamlCanvas_OnPaintSurface" />
</Grid>
</UserControl>
【问题讨论】:
-
我不清楚你到底想要达到什么目的,所以很难回答。然而这个问题“据我所知,没有办法访问父母的属性。这是正确的吗?”是不正确的。你可以
x:Name你的UserControl(或者Grid,如果你想要那个)并在模板的绑定中引用它,获得对父控件属性的访问。在移动自动取款机上很难查到,但我以前用这个结构回答过问题。 -
感谢您的回复。我想要一个项目的 ListControl,其中它们的
Height是根据以下条件计算的:项目中的RowCount变量和控件中的RowHeight变量。我会尝试 x:Name 方法。谢谢! -
嗯,这似乎不起作用。查看更新
-
你只是命名了它,但你没有在绑定中引用它。也许这可能会有所帮助:social.msdn.microsoft.com/Forums/en-US/…
-
在使用 x:Bind(而不是 Binding)时如何引用它。因为您只能使用 x:Bind 调用函数。 stackoverflow.com/a/32851493/1757179 我不能将绑定与 ValueConverter 一起使用,因为 UWP 不支持 MultiValueConverters。
标签: c# xaml uwp dependency-properties