WPF自定义控件 - 如何获取正确的宽度比例

3
我正在创建一个控件来表示员工的工作班次。这个班次可以有不同的长度和没有或多个休息时间。
以下问题涉及原型中的蓝色条: 由于控件需要完美地调整大小,因此固定大小的方法不可行。我的第一个想法是使用网格,其中列具有与时间跨度相同的宽度比例。因此,如果您查看上面的原型,将有3列,宽度分别为:240*、60*、240*。这些数字等于每个时间跨度的总分钟数。
如果我添加一个依赖属性,称之为TimeSpanItems(TSI),每个TSI都有一个TimeSpan属性。那么是否可能将其绑定到网格及其列定义?随着添加TSI,列的数量必须发生变化,并且每个列必须更改其宽度比例以匹配分钟数。
我是否想错了?这可行吗?还是我需要一个项目控件,在调整控件大小时调整其项目?
目前我有不同的问题,但尚未找到答案...可能还有很多问题我还不知道。任何帮助都将不胜感激。
2个回答

0

我做了些对你有帮助的事情,最终的结果就是这个控件:

Task Timeline

基本上列出任务,其中显示了顶部列出的“实体”进行的具体分配。

任务列表是一个类似于以下内容的ItemsControl:

<ItemsControl ItemsSource="{Binding Path=Tarefas}" Grid.Column="4">
    <ItemsControl.Template>
        <ControlTemplate TargetType="ItemsControl">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="30" />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Border Grid.Row="0" Background="SlateGray" Margin="5 5 5 5">
                    <TextBlock Grid.ColumnSpan="100" Text="Cronograma" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" />
                </Border>
                <ItemsPresenter Grid.Row="1" />
            </Grid>
        </ControlTemplate>
    </ItemsControl.Template>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid Style="{StaticResource CronogramaGrid}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="{Binding Converter={StaticResource InicioPerc}}" />
                    <ColumnDefinition Width="{Binding Converter={StaticResource MeioPerc}}" />
                    <ColumnDefinition Width="{Binding Converter={StaticResource FimPerc}}" />
                </Grid.ColumnDefinitions>
                <Border Background="SlateGray" Grid.Column="1">
                    <Border Background="White" Margin="2 2 2 2">
                        <Grid Margin="2 2 2 2">
                            <Border Background="SlateGray" />
                            <ItemsControl ItemsSource="{Binding Path=PercentagensParticipacao}">
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Grid Height="{Binding Path=GridHeight}">
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="{Binding Path=Actual, Converter={StaticResource GridLengthStarConverter}}" />
                                                <ColumnDefinition Width="{Binding Path=Restante, Converter={StaticResource GridLengthStarConverter}}" />
                                            </Grid.ColumnDefinitions>
                                            <Border Grid.Column="0" Background="{Binding Path=Index, Converter={StaticResource IndexColorConverter}}" />
                                        </Grid>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </Grid>
                    </Border>
                </Border>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

这个转换器给出了列宽。在构建此控件时,我们没有扩展模型的能力,因此编写了一个转换器。如果现在进行此操作,模型将被扩展并编写Getter。

转换器看起来像这样:

public class DataInicioPercentagemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var tarefa = (Tarefa)value;
        var candidatura = Indra.Injection.ServiceLocator.MefContainer.GetExportedValue<IDataManager>().Candidatura;

        double totalDias = candidatura.DataFim.Subtract(candidatura.DataInicio).Days;
        double diasTarefaInicio = tarefa.Inicio.Subtract(candidatura.DataInicio).Days;

        return new GridLength((diasTarefaInicio / totalDias * 100), GridUnitType.Star);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }
}

public class DataMeioPercentagemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var tarefa = (Tarefa)value;
        var candidatura = Indra.Injection.ServiceLocator.MefContainer.GetExportedValue<IDataManager>().Candidatura;

        double totalDias = candidatura.DataFim.Subtract(candidatura.DataInicio).Days;
        double diasTarefa = tarefa.Fim.Subtract(tarefa.Inicio).Days;

        return new GridLength((diasTarefa / totalDias * 100), GridUnitType.Star);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }
}

public class DataFimPercentagemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var tarefa = (Tarefa)value;
        var candidatura = Indra.Injection.ServiceLocator.MefContainer.GetExportedValue<IDataManager>().Candidatura;

        double totalDias = candidatura.DataFim.Subtract(candidatura.DataInicio).Days;
        double diasTarefaFim = candidatura.DataFim.Subtract(tarefa.Fim).Days;

        return new GridLength((diasTarefaFim / totalDias * 100), GridUnitType.Star);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }
}

0
我将回答这个问题:它可行吗?我的初步想法是,我不想计算调整大小。我希望使用网格来解决这个问题。因此,我将忽略所有其他关于是否应该是另一种控件类型或者这是否是“正确”的方法的问题。
接下来...首先,我使用一个控件模板,其中uxMainContentGrid是在添加项目时要修改的网格。
<Style TargetType="{x:Type wpflib:RatioPresenterControl}">
<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="{x:Type wpflib:RatioPresenterControl}">
            <Border Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    Height="{TemplateBinding Height}"
                    Width="{TemplateBinding Width}">

                <Grid x:Name="uxMainContentGrid">
                    <Grid.ColumnDefinitions>
                        <!-- Columns and ratio is set in code behind -->
                    </Grid.ColumnDefinitions>
                </Grid>
            </Border>
        </ControlTemplate>
    </Setter.Value>
</Setter>

我用来设置宽度比例的项目看起来像这样。

Public Class RatioItem
    Public Property Value As Double
    Public Property Brush As Brush
End Class

而且,RatioItems 是通过这个依赖属性添加的,默认值是一个空的 RatioItems 列表。

 Public Shared ReadOnly RatioItemsProperty As DependencyProperty = _
        DependencyProperty.Register("RatioItems", GetType(IEnumerable(Of RatioItem)),
              GetType(RatioPresenterControl),
              New FrameworkPropertyMetadata(New List(Of RatioItem), AddressOf OnRatioItemsPropertyChanged))

该依赖属性具有以下值更改回调方法。当您绑定到依赖属性时,此回调会在模板应用之前触发。仅当控件模板已设置时,此回调才会重构网格列。
Public Shared Sub OnRatioItemsPropertyChanged(sender As Object, e As DependencyPropertyChangedEventArgs)
    If (_MainContentGrid IsNot Nothing) Then
        Dim ratioItems As IEnumerable(Of RatioItem) = TryCast(e.NewValue, IEnumerable(Of RatioItem))

        ReconstructGridColumns(ratioItems, _MainContentGrid)
    End If
End Sub

以下代码在应用控件模板时检索网格。这里的关键是存储网格,以便在RatioItems属性更改后可以稍后访问它。当应用模板时,绑定已经生效,因此网格列已构建。
Public Overrides Sub OnApplyTemplate()
    MyBase.OnApplyTemplate()

    _MainContentGrid = TryCast(Me.Template.FindName("uxMainContentGrid", Me), Grid)

    ReconstructGridColumns(Me.RatioItems, _MainContentGrid)
End Sub

这个方法执行所有的“构造”操作...

Private Shared Sub ReconstructGridColumns(ByVal ratioItems As IEnumerable(Of RatioItem), ByVal mainContentGrid As Grid)
    Dim newContent As Rectangle
    Dim columnCount As Integer = 0

    mainContentGrid.ColumnDefinitions.Clear()

    For Each item In ratioItems
        mainContentGrid.ColumnDefinitions.Add(New ColumnDefinition() With {.Width = New GridLength(item.Value, GridUnitType.Star)})
        newContent = New Rectangle() With {.Name = "item" & columnCount, .Fill = item.Brush}
        mainContentGrid.Children.Add(newContent)
        Grid.SetColumn(newContent, columnCount)
        columnCount += 1
    Next
End Sub

就是这样。它可以完成。现在讨论的是是否这是“正确”的方法... :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接