在加载时获得实际高度

时间:2017-12-19 13:55:12

标签: c# wpf wpfdatagrid actualheight

我正在为数据网格进行用户控制。此数据网格包含一些新旧条目。要求新条目显示在屏幕顶部,但数据顺序必须按时间顺序排列。所以我通过添加空行向上推动整个网格。例如,如果我有8个旧条目和2个新条目,那么8个旧条目必须是"隐藏"高于两个新条目中的第一个。

要计算空行,我想将用户控件的实际高度除以行高。我找到了一种通过依赖属性获取实际高度的方法(参见下面的代码)。我需要它在开始时可用,所以我实现了一个触发器,让我访问load事件。然而此时用户控件的大小仍为0.那么还有另一个事件可以让我在正确的时间计算出来吗?

最后一点,我正在使用Galasoft MVVMLight和匹配的mvvm模式。一旦你在第一次初始化后改变窗口的高度,依赖属性确实给出了正确的高度。

视图

<DataGrid x:Class="Kwa.Presentation.Views.AlarmList.AlarmList"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
          xmlns:local="clr-namespace:Kwa.Presentation.Views.AlarmList"
          xmlns:behaviors="clr-namespace:Kwa.Presentation.Behaviors"
          xmlns:Trans="clr-namespace:Kwa.Presentation.Resources"
          xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
          mc:Ignorable="d" 
          d:DesignHeight="500" d:DesignWidth="750"
          ItemsSource="{Binding Alarms}"
          SelectedItem="{Binding SelectedAlarm, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          SelectionChanged = "AlarmFramework_SelectionChanged"
          IsSynchronizedWithCurrentItem="True"
          CanUserResizeColumns="True" IsReadOnly="True" CanUserReorderColumns="False" CanUserSortColumns="False" SelectionMode="Single" CanUserAddRows="False"
          Background="White" RowHeaderWidth="0" AutoGenerateColumns="False" GridLinesVisibility="None" RowHeight="20" FrozenColumnCount = "1"
          ScrollViewer.CanContentScroll="False" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto"
          x:Name="AlarmFramework"
          behaviors:ElementActualSizeBehavior.ActualHeight="{Binding ListHeight}"

          >

        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Loaded">
                <i:InvokeCommandAction Command="{Binding AlarmListLoadedCommand}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>

    <DataGrid.Columns>

        <DataGridTemplateColumn Header="{x:Static Trans:TranslatedResources.AlarmColumnHeaderTime}" Width="auto" HeaderStyle="{StaticResource WithButt}">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <components:FlagControl VerticalAlignment="Center" Height="15" Width="15" FlagColor="{Binding Severity, Converter={StaticResource AlarmSeverityToColorConverter}}"
                                                Visibility="{Binding AckStatus, Converter={StaticResource InvertedBoolToVisibilityConverter}, Mode=TwoWay}"/>
                        <TextBlock Text="{Binding DateTimeString}" Padding="10,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center"/>
                    </StackPanel>

                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <DataGridTextColumn Header="{x:Static Trans:TranslatedResources.AlarmColumnHeaderSeverity}" Binding="{Binding SeverityString}" Width="auto" HeaderStyle="{StaticResource WithoutButt}"/>

        <DataGridTextColumn Header="{x:Static Trans:TranslatedResources.AlarmColumnHeaderDescription}" Binding="{Binding Description}"  d:Width="400" Width="*" HeaderStyle="{StaticResource WithoutButt}"/>



    </DataGrid.Columns>
</DataGrid>

获取实际高度的依赖属性

public class ElementActualSizeBehavior
{

    public static double GetActualWidth(DependencyObject obj)
    {
        return (double)obj.GetValue(ActualWidthProperty);
    }

    public static void SetActualWidth(DependencyObject obj, double value)
    {
        obj.SetValue(ActualWidthProperty, value);
    }

    public static double GetActualHeight(DependencyObject obj)
    {
        return (double)obj.GetValue(ActualHeightProperty);
    }

    public static void SetActualHeight(DependencyObject obj, double value)
    {
        obj.SetValue(ActualHeightProperty, value);
    }

    public static readonly DependencyProperty ActualWidthProperty = DependencyProperty.RegisterAttached("ActualWidth", typeof(double), typeof(ElementActualSizeBehavior), new FrameworkPropertyMetadata(double.NaN, new PropertyChangedCallback(ActualWidthChanged)) { BindsTwoWayByDefault = true });
    public static readonly DependencyProperty ActualHeightProperty = DependencyProperty.RegisterAttached("ActualHeight", typeof(double), typeof(ElementActualSizeBehavior), new FrameworkPropertyMetadata(double.NaN, new PropertyChangedCallback(ActualHeightChanged)) { BindsTwoWayByDefault = true });

    private static void ActualHeightChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        Control w = source as Control;
        if (w != null)
        {
            w.SizeChanged += (se, ev) =>
            {
                SetActualHeight((DependencyObject)se, ev.NewSize.Height);
            };
        }
    }

    private static void ActualWidthChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        Control w = source as Control;
        if (w != null)
        {
            w.SizeChanged += (se, ev) =>
            {
                SetActualWidth((DependencyObject)se, ev.NewSize.Width);
            };
        }
    }
}

视图模型

public class AlarmListViewModel : MainViewModelBase
    {
    #region Properties

    private double _listHeight;
    public double ListHeight
    {
        get { return _listHeight; }
        set
        {
            if (Double.IsNaN(value))
            {
                //Debug.WriteLine("nan");
                return;
            }

            _listHeight = value;
            //Debug.WriteLine(value);
            RaisePropertyChanged(() => ListHeight);
        }
    }


    private ObservableCollection<AlarmEntryViewModel> _alarms;
    public ObservableCollection<AlarmEntryViewModel> Alarms
    {
        get { return _alarms; }
        set { Set(() => Alarms, ref _alarms, value); }
    }

    private AlarmEntryViewModel _selectedAlarm;
    public AlarmEntryViewModel SelectedAlarm
    {
        get { return _selectedAlarm; }
        set { Set(() => SelectedAlarm, ref _selectedAlarm, value); }
    }

    private int _acknowledgeAllowed;
    public int AcknowledgeAllowed
    {
        get { return _acknowledgeAllowed; }
        set { Set(() => AcknowledgeAllowed, ref _acknowledgeAllowed, value); }
    }

    private int _acknowledgableAlarms;
    public int AcknowledgableAlarms
    {
        get { return _acknowledgableAlarms; }
        set { Set(() => AcknowledgableAlarms, ref _acknowledgableAlarms, value); }
    }

    private int _rowHeight;
    public int RowHeight
    {
        get { return _rowHeight; }
        set { Set(() => RowHeight, ref _rowHeight, value); }
    }

    private readonly IActionCommand _acknowledgeCommand;
    public IActionCommand AcknowledgeCommand
    {
        get { return _acknowledgeCommand; }
    }

    private readonly IActionCommand _alarmListLoadedCommand;
    public IActionCommand AlarmListLoadedCommand
    {
        get { return _alarmListLoadedCommand; }
    }

    public int MaxAcknowledgedAlarm;
    public int AlarmToSendIndex { get; set; }

    #endregion

    #region Constructor

    public AlarmListViewModel()
    {
        //Lock collection to stop inconsistent item source exception
        Alarms = new ObservableCollection<AlarmEntryViewModel>();
        BindingOperations.EnableCollectionSynchronization(Alarms, _AlarmsLock);


        //Add command
        _acknowledgeCommand = new ActionCommand<AlarmEntryViewModel>(p => Acknowledge(p));
        _alarmListLoadedCommand = new ActionCommand<AlarmListViewModel>(p => Startup());
    }

    #endregion

    #region Private Methods



    private void Startup()
    {
        var rowsInView = (int)Math.Floor(ListHeight / RowHeight);
        var rowsInAlarms = Alarms.Count;
        var rowsNeeded = rowsInView - rowsInAlarms;
    }

1 个答案:

答案 0 :(得分:0)

最终有人向我解释了这个问题并提出了解决方案。问题在于用户控件的高度/宽度在加载时实际上没有变化。它保持相对于它的祖先宽度/高度(或类似的东西)。无论如何,这就是你在视图模型中没有得到更新的原因。

解决方案分为两部分。首先,必须按照上面的代码实现ElementActionSizeBehavior。然后,您必须显式声明组件大小。这可以通过添加以下行来完成:

Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Control}}, Path=ActualHeight}"

因此更新的视图标题应该是这样的:

<DataGrid x:Class="Kwa.Presentation.Views.AlarmList.AlarmList"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
          xmlns:local="clr-namespace:Kwa.Presentation.Views.AlarmList"
          xmlns:behaviors="clr-namespace:Kwa.Presentation.Behaviors"
          mc:Ignorable="d" 
          d:DesignHeight="500" d:DesignWidth="750"
          Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Control}}, Path=ActualHeight}"
behaviors:ElementActualSizeBehavior.ActualHeight="{Binding ListHeight}"
          >

您也不再需要交互部分了。

希望这可以帮助其他人获得他们的实际高度和宽度。