简体   繁体   中英

VisualStateManager.GoToState not working on DataGrid within ChildWindow

I extended the System.Windows.Controls.DataGrid control in order to add a static empty collection message when the ItemsSource is null or empty. The logic to determine whether the message is displayed or not is done using a visual state, as shown below. This logic works perfectly within pages or user controls however when my extended DataGrid is used directly on a ChildWindow, the message does not appear even though the logic is fired without error.

Extended 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 Style

    <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>

I suspect that the observed behavior is not caused by the ChildWindow or -in general- the placement anywhere in your UI.

When I looked at your re-definition of ItemsSource I noticed that you placed some code there that needs to be executed in order to trigger the VisualState change (and to update the base class property in an attempt to keep the local value and the DependencyProperty in sync). But that's not how DependencyProperties work. Bindings will not call setters or getters, they directly call SetValue(...) and GetValue(...) ( MSDN ). So any binding you use will not trigger the setter's code in your derived class.

I would not derive a new class and provide a near-duplicate of the whole ControlTemplate just to add a "no items here" sign. A mere decoration. I would rather solve this with something like:

<MyFancyView>
    ...
    <DataGrid ItemsSource="{...}">
        <Behaviors>
            <ShowEmptySign/>
        <Behaviors>
    </DataGrid>

</MyFancyView>

and a custom behavior

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;
    }
}

I checked MSDN and saw that DataGrid is not derived from ItemsControl . So to actually use it the behavior has to be changed to rather use rowLoaded/rowUnloaded events instead of collectionChanged events.

You can try to execute your state change code not in the setter but whenever a row is loaded or unloaded. So any changes in the ItemsSource will eventually trigger you code.

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);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM