[英]Get actual height at load time
我正在為數據網格創建用戶控件。 該數據網格具有一些新舊條目。 要求新條目顯示在屏幕頂部,但數據順序必須是按時間順序排列的。 因此,我通過添加空行將整個網格向上推。 例如,如果我有8個舊條目和2個新條目,則必須將8個舊條目“隱藏”在兩個新條目中的第一個之上。
要計算空行,我想將用戶控件的實際高度除以行高。 我找到了一種通過依賴項屬性獲取實際高度的方法(請參見下面的代碼)。 我一開始需要它可用,因此我實現了一個觸發器以使我能夠訪問load事件。 但是此時用戶控件的大小仍為0。那么是否還有另一個事件可以讓我根據正確的時間進行計算?
最后一點,我正在使用Galasoft MVVMLight和匹配的mvvm模式。 同樣,在您首次初始化后更改窗口的高度后,dependency屬性的確會提供正確的高度。
風景
<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;
}
最終有人向我解釋了問題並給出了解決方案。 問題在於用戶控件的高度/寬度在加載時實際上沒有改變。 它保持相對於其祖先的寬度/高度(或類似的東西)。 無論如何,這就是為什么您沒有在視圖模型中獲得更新的原因。
解決方案存在兩個部分。 首先,必須像上面的代碼中那樣實現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}"
>
您也不再需要交互部分。
希望這可以幫助其他人獲得他們的實際身高和寬度。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.