我扩展了System.Windows.Controls.DataGrid控件,以便在ItemsSource为null或为空时添加静态空集合消息。确定是否显示消息的逻辑是使用可视状态完成的,如下所示。这个逻辑在页面或用户控件中完美地工作,但是当我的扩展DataGrid直接在ChildWindow上使用时,即使逻辑被触发没有错误,也不会出现消息。
扩展DataGrid
public class ReACTDataGrid : DataGrid
{
public bool IsFromChildWindow { get; set; }
private IEnumerable _ItemsSource;
public IEnumerable ItemsSource
{
get { return _ItemsSource; }
set
{
_ItemsSource = value;
base.ItemsSource = value;
if (_ItemsSource.IsNullOrEmpty())
VisualStateManager.GoToState(this, "HasNoItems", false);
else
VisualStateManager.GoToState(this, "HasItems", false);
}
}
}
XAML风格
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="data:DataGrid">
<!--<Grid Background="{Binding Converter={StaticResource ThemeColorConverter}, ConverterParameter=Sub_Brush}" x:Name="Root">-->
<Grid Background="White" x:Name="Root">
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="EmptyMsg">
<vsm:VisualState x:Name="HasNoItems">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="EmptyCollectionMsg" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="HasItems">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="EmptyCollectionMsg" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Grid.Resources>
<ControlTemplate x:Key="TopLeftHeaderTemplate" TargetType="dataPrimitives:DataGridColumnHeader">
<Grid Margin="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Rectangle x:Name="Background" Opacity="0.65" Grid.ColumnSpan="3" Grid.RowSpan="3">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1.4" StartPoint="0.5,0">
<GradientStop Color="{Binding Converter={StaticResource ThemeColorConverter}, ConverterParameter=Sub_Color}" Offset="0.75" />
<GradientStop Color="{Binding Converter={StaticResource ThemeColorConverter}, ConverterParameter=Sub_Color}" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Border CornerRadius="10,10,40,40" x:Name="Highlight" RenderTransformOrigin="0.5,1" Grid.ColumnSpan="3">
<Border.Background>
<RadialGradientBrush>
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.25" ScaleY="2" />
<TranslateTransform Y="-0.6" />
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Color="#BFFFFFFF" Offset="0" />
<GradientStop Color="#4CFFFFFF" Offset="1" />
</RadialGradientBrush>
</Border.Background>
</Border>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="TopRightHeaderTemplate" TargetType="dataPrimitives:DataGridColumnHeader">
<Grid Margin="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Set opacity to zero to hide mess above scrollbar -->
<Rectangle x:Name="Background" Opacity="0" Grid.ColumnSpan="3" Grid.RowSpan="3">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1.4" StartPoint="0.5,0">
<GradientStop Color="{Binding Converter={StaticResource ThemeColorConverter}, ConverterParameter=Main_Color}" Offset="0.75" />
<GradientStop Color="{Binding Converter={StaticResource ThemeColorConverter}, ConverterParameter=Sub_Color}" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Border CornerRadius="0,0,40,40" x:Name="Highlight" Opacity="0" RenderTransformOrigin="0.5,1" Grid.ColumnSpan="3">
<Border.Background>
<RadialGradientBrush>
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.25" ScaleY="2" />
<TranslateTransform Y="-0.6" />
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Color="#BFFFFFFF" Offset="0" />
<GradientStop Color="#4CFFFFFF" Offset="1" />
</RadialGradientBrush>
</Border.Background>
</Border>
</Grid>
</ControlTemplate>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<dataPrimitives:DataGridColumnHeader x:Name="TopLeftCornerHeader" Width="22" Template="{StaticResource TopLeftHeaderTemplate}" />
<dataPrimitives:DataGridColumnHeadersPresenter Grid.Column="1" x:Name="ColumnHeadersPresenter" />
<dataPrimitives:DataGridColumnHeader Grid.Column="2" x:Name="TopRightCornerHeader" Template="{StaticResource TopRightHeaderTemplate}" />
<Rectangle Grid.ColumnSpan="3" Height="1" x:Name="ColumnHeadersAndRowsSeparator" VerticalAlignment="Bottom" Width="Auto" Fill="#FFDBDCDC" StrokeThickness="1" />
<dataPrimitives:DataGridRowsPresenter Grid.ColumnSpan="2" Grid.Row="1" x:Name="RowsPresenter" />
<Rectangle Grid.Column="2" Grid.Row="2" x:Name="BottomRightCorner" Fill="{TemplateBinding Background}" />
<Rectangle Grid.ColumnSpan="2" Grid.Row="2" x:Name="BottomLeftCorner" Fill="{TemplateBinding Background}" />
<ScrollBar Grid.Column="2" Grid.Row="1" Margin="0" x:Name="VerticalScrollbar" Width="18" Orientation="Vertical" />
<Grid Grid.Column="1" Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Rectangle x:Name="FrozenColumnScrollBarSpacer" />
<ScrollBar Grid.Column="1" Height="18" Margin="0" x:Name="HorizontalScrollbar" Orientation="Horizontal" />
</Grid>
<TextBlock x:Name="EmptyCollectionMsg" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2" Text="No Data Entered" Visibility="Collapsed"></TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
答案 0 :(得分:0)
我怀疑所观察到的行为不是由ChildWindow
或-in general引起的 - 您的用户界面中的任何位置。
当我查看您对ItemsSource
的重新定义时,我注意到您在那里放置了一些需要执行的代码,以便触发VisualState
更改(以及更新基类属性)尝试保持本地值和DependencyProperty同步)。
但那并不是DependencyProperties
的工作方式。 Bindings
不会调用setter或getter,它们直接调用SetValue(...)和GetValue(...)(MSDN)。因此,您使用的任何绑定都不会触发派生类中的setter代码。
我不会派出一个新类并提供整个ControlTemplate
的近似副本,只是为了添加“此处没有项目”的标志。仅仅装饰。我宁愿通过以下方式解决这个问题:
<MyFancyView>
...
<DataGrid ItemsSource="{...}">
<Behaviors>
<ShowEmptySign/>
<Behaviors>
</DataGrid>
</MyFancyView>
和自定义行为
public class ShowEmptySign : Behavior<ItemsControl>
{
private TextBlock msg;
...OnAttached()
{
var rootGrid=AssociatedObject.GetDecendant<Grid>();
msg = new TextBlock(){...};
rootGrid.Children.Add(msg)
((INotifyCollectionChanged)AssociatedObject.Items).CollectionChanged += CheckIfEmpty
CheckIfEmpty();
}
...CheckIfEmpty()
{
if(!AssociatedObject.Items.Any()) Show(); else Hide();
}
...Show()
{
msg.Visibility = Visibility.Visible;
}
}
我查看了MSDN,发现DataGrid
并非来自ItemsControl
。因此,要实际使用它,必须将行为更改为使用rowLoaded / rowUnloaded事件而不是collectionChanged事件。
答案 1 :(得分:0)
您可以尝试执行不在setter中的状态更改代码,但只要加载或卸载行即可。因此,ItemsSource中的任何更改最终都会触发您的代码。
public class ReACTDataGrid : DataGrid
{
private bool templateApplied;
protected override void OnApplyTemplate()
{
templateApplied = true;
base.OnApplyTemplate();
CheckIfItemsEmptyAndUpdateVisualState();
}
protected override void OnLoadingRow(DataGridRowEventArgs e)
{
if (templateApplied) CheckIfItemsEmptyAndUpdateVisualState();
}
protected override void OnUnloadingRow(DataGridRowEventArgs e)
{
if (templateApplied) CheckIfItemsEmptyAndUpdateVisualState();
}
private void CheckIfItemsEmptyAndUpdateVisualState()
{
if (this.ItemsSource.IsNullOrEmpty())
VisualStateManager.GoToState(this, "HasNoItems", false);
else
VisualStateManager.GoToState(this, "HasItems", false);
}
}