我正在尝试实现一个用户控件,该控件代表时间线(例如在视频编辑器中),其中的片段的开始和结束标记可以由用户拖动。
我在我的 View 模型中表示这样的片段:
public class Moment : ViewModelBase
{
[Reactive] public double From { get; set; }
[Reactive] public double Duration { get; set; }
}
并尝试使用带有网格分割器的网格来实现 View ,如下所示:
<Grid ColumnDefinitions="Auto,3,Auto,3,*" HorizontalAlignment="Stretch">
<Panel Grid.Column="0" Name="SpacerLeft"
Width="{Binding From}" />
<GridSplitter Grid.Column="1" Background="cyan" />
<Rectangle Grid.Column="2" Name="SpacerSegment"
HorizontalAlignment="Stretch" Fill="red" Height="40"
Width="{Binding Duration}">
</Rectangle>
<GridSplitter Grid.Column="3" Background="cyan" />
<Panel Grid.Column="4" Name="SpacerRight"/>
</Grid>
这就像我可以在视觉上调整片段的大小一样:
但是,我正在努力将尺寸更改返回到 View 模型中,您也可以在红色区域中看到尺寸没有改变。我找不到方法来检索 SpacerLeft
或 SpacerSegment
控件的宽度更改。
我尝试的是删除专用的Width
属性并绑定(bind)网格的ColumnDefinitions
。我将此属性添加到我的 View 模型中:
public ColumnDefinitions ColumnDefinitions
{
get => ColumnDefinitions.Parse($"{From},3,{Duration},3,*");
set
{
From = value[0].ActualWidth;
Duration = value[2].ActualWidth;
}
}
并将 View XAML 更改为:
<Grid ColumnDefinitions="{Binding ColumnDefinitions}" HorizontalAlignment="Stretch">
<Panel Grid.Column="0" Name="SpacerLeft" />
<GridSplitter Grid.Column="1" Background="cyan" />
<Rectangle Grid.Column="2" Name="SpacerSegment"
HorizontalAlignment="Stretch" Fill="red" Height="40">
</Rectangle>
<GridSplitter Grid.Column="3" Background="cyan" />
<Panel Grid.Column="4" Name="SpacerRight"/>
</Grid>
但是由于不幸的是,这对我来说没有多大意义,所以无法编译:
InvalidCastException: Unable to cast object of type 'Avalonia.Data.Binding' to type 'Avalonia.Controls.ColumnDefinition'. System.InvalidCastException: Unable to cast object of type 'Avalonia.Data.Binding' to type 'Avalonia.Controls.ColumnDefinition'.
at Avalonia.Collections.AvaloniaList`1.System.Collections.IList.Add(Object value) in /_/src/Avalonia.Base/Collections/AvaloniaList.cs:line 520
at Builder_1ee6d795025442edb279bcc7110e88eb_avares://AvaloniaOutseekClient/Views/MomentsSourceView.axaml.XamlClosure_2.Build(IServiceProvider )
at Avalonia.Markup.Xaml.XamlIl.Runtime.XamlIlRuntimeHelpers.<>c__DisplayClass0_0.<DeferredTransformationFactoryV1>b__0(IServiceProvider sp) in /_/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs:line 28
at Avalonia.Markup.Xaml.Templates.TemplateContent.Load(Object templateContent) in /_/src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs:line 17
at Avalonia.Markup.Xaml.Templates.DataTemplate.Build(Object data, IControl existing) in /_/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs:line 33
at Avalonia.Controls.Presenters.ContentPresenter.CreateChild() in /_/src/Avalonia.Controls/Presenters/ContentPresenter.cs:line 356
...
最佳答案
第一个问题是尝试直接绑定(bind)到Grid的ColumnDefinitions
。相反,使用长定义形式并直接绑定(bind)到每列的宽度是正确的方法:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ...}" />
<ColumnDefinition Width="Auto" />
<!-- ... -->
</Grid.ColumnDefinitions>
</Grid>
还需要显式指定 Mode=TwoWay
,因为默认情况下,绑定(bind)到 ColumnDefinition
的宽度不是双向的。
接下来,由于 ColumnDefinition
的 Width
属性实际上是 GridLength
类型,而不是 double
,因此如所述 here 需要 IValueConverter
.
此时网格的 XAML 应该如下所示
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding From, Mode=TwoWay, Converter={StaticResource GridLengthConverter}}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="{Binding Duration, Mode=TwoWay, Converter={StaticResource GridLengthConverter}}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Panel Grid.Column="0" Name="SpacerLeft" />
<GridSplitter Grid.Column="1" Background="cyan" />
<Rectangle Grid.Column="2" Name="SpacerSegment"
HorizontalAlignment="Stretch" Fill="red" Height="40">
</Rectangle>
<GridSplitter Grid.Column="3" Background="cyan" />
<Panel Grid.Column="4" Name="SpacerRight"/>
</Grid>
此外,为了修复左网格分割器移动选择而不是将其扩展到左侧的问题,Moment
View 模型需要使用 From
和 To
属性而不是 From
和 Duration
属性,并让 Duration
属性从其他两个属性派生。这需要手动连接一些属性更改事件,而不是依赖于[Reactive]
:
private double _from;
private double _to;
public double From
{
get => _from;
set
{
if (Equals(_from, value))
return;
this.RaisePropertyChanging(nameof(Duration));
this.RaisePropertyChanging();
_from = value;
this.RaisePropertyChanged();
this.RaisePropertyChanged(nameof(Duration));
}
}
public double To
{
get => _to;
set
{
if (Equals(_to, value))
return;
this.RaisePropertyChanging(nameof(Duration));
this.RaisePropertyChanging();
_to = value;
this.RaisePropertyChanged();
this.RaisePropertyChanged(nameof(Duration));
}
}
public double Duration
{
get => To - From;
set => To = From + value;
}
经过这些更改,结果如下所示:
关于c# - 时间线中可调整大小的片段 : bind segment size to view model,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67257867/