Как я могу изменить VisualState в Представлении от ViewModel?

Я плохо знаком с WPF и MVVM. Я думаю, что это - простой вопрос. Мой ViewModel выполняет асинхронный вызов для получения данных для DataGrid, который связывается с ObservableCollection в ViewModel. Когда данные загружаются, я установил надлежащее свойство ViewModel, и DataGrid отображает данные без проблемы. Однако я хочу представить визуальный индикатор для пользователя, которого загружают данные. Так, с помощью Смешения я добавил это к своей разметке:

        <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="LoadingStateGroup">
            <VisualState x:Name="HistoryLoading">
                <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="HistoryGrid">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
            <VisualState x:Name="HistoryLoaded">
                <Storyboard>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="WorkingStackPanel">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

Я думаю, что знаю, как изменить состояние в моем коде - позади (что-то подобное этому):

VisualStateManager.GoToElementState(LayoutRoot, "HistoryLoaded", true);

Однако место, где я хочу сделать, это находится в методе завершения ввода-вывода моего ViewModel, который не имеет ссылки на, он - соответствующее Представление. Как я выполнил бы это использование шаблона MVVM?

9
задан Howard Pinsley 15 June 2010 в 16:33
поделиться

3 ответа

Вы можете сделать что-то вроде этого:

XAML

<Window x:Class="WpfSOTest.BusyWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfSOTest"
    Title="BusyWindow"
    Height="300"
    Width="300">
<Window.Resources>
    <local:VisibilityConverter x:Key="VisibilityConverter" />
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Border Grid.Row="0">
        <Grid>
            <Border>
                <Rectangle Width="400"
                           Height="400"
                           Fill="#EEE" />
            </Border>
            <Border Visibility="{Binding IsBusy, Converter={StaticResource VisibilityConverter}}">
                <Grid>
                    <Rectangle Width="400"
                               Height="400"
                               Fill="#AAA" />
                    <TextBlock Text="Busy"
                               HorizontalAlignment="Center"
                               VerticalAlignment="Center" />
                </Grid>
            </Border>
        </Grid>
    </Border>
    <Border Grid.Row="1">
        <Button Click="ChangeVisualState">Change Visual State</Button>
    </Border>
</Grid>

Код:

public partial class BusyWindow : Window
{
    ViewModel viewModel = new ViewModel();

    public BusyWindow()
    {
        InitializeComponent();

        DataContext = viewModel;
    }

    private void ChangeVisualState(object sender, RoutedEventArgs e)
    {
        viewModel.IsBusy = !viewModel.IsBusy;
    }
}

public class ViewModel : INotifyPropertyChanged
{
    protected Boolean _isBusy;
    public Boolean IsBusy
    {
        get { return _isBusy; }
        set { _isBusy = value; RaisePropertyChanged("IsBusy"); }
    }

    public ViewModel()
    {
        IsBusy = false;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(String propertyName)
    {
        PropertyChangedEventHandler temp = PropertyChanged;
        if (temp != null)
        {
            temp(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

class VisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        switch (((Boolean)value))
        {
            case true:
                return Visibility.Visible;
        }

        return Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

----------------------- Обновленный код --- --------------------

XAML

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Border Grid.Row="0">
        <Grid>
            <local:MyBorder IsBusy="{Binding IsBusy}">
                <Grid>
                    <TextBlock Text="{Binding IsBusy}"
                               HorizontalAlignment="Center"
                               VerticalAlignment="Center" />
                </Grid>
            </local:MyBorder>
        </Grid>
    </Border>
    <Border Grid.Row="1">
        <Button Click="ChangeVisualState">Change Visual State</Button>
    </Border>
</Grid>

Шаблон

<Style TargetType="{x:Type local:MyBorder}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyBorder}">
                <Border Name="RootBorder">
                    <Border.Background>
                        <SolidColorBrush x:Name="NormalBrush"
                                         Color="Transparent" />
                    </Border.Background>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup Name="CommonGroups">
                            <VisualState Name="Normal" />
                        </VisualStateGroup>
                        <VisualStateGroup Name="CustomGroups">
                            <VisualState Name="Busy">
                                <VisualState.Storyboard>
                                    <Storyboard>
                                        <ColorAnimation Storyboard.TargetName="NormalBrush"
                                                        Storyboard.TargetProperty="Color"
                                                        Duration="0:0:0.5"
                                                        AutoReverse="True"
                                                        RepeatBehavior="Forever"
                                                        To="#EEE" />
                                    </Storyboard>
                                </VisualState.Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <ContentPresenter />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Пользовательский элемент

[TemplateVisualState(GroupName = "CustomGroups", Name = "Busy")]
public class MyBorder : ContentControl
{
    static MyBorder()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MyBorder), new FrameworkPropertyMetadata(typeof(MyBorder)));
    }

    public Boolean IsBusy
    {
        get { return (Boolean)GetValue(IsBusyProperty); }
        set { SetValue(IsBusyProperty, value); }
    }

    public static readonly DependencyProperty IsBusyProperty =
        DependencyProperty.Register("IsBusy", typeof(Boolean), typeof(MyBorder), new UIPropertyMetadata(IsBusyPropertyChangedCallback));

    static void IsBusyPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as MyBorder).OnIsBusyPropertyChanged(d, e);
    }

    private void OnIsBusyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (Convert.ToBoolean(e.NewValue))
        {
            VisualStateManager.GoToState(this, "Busy", true);
        }
        else
        {
            VisualStateManager.GoToState(this, "Normal", true);
        }
    }
}
7
ответ дан 4 December 2019 в 10:03
поделиться

Стандартный способ сделать это - обычно иметь свойство во вьюмодели (либо зависимое свойство, либо участвующее в INotifyPropertyChanged), которое будет сигнализировать о том, что данные загружаются - возможно bool IsLoadingData или подобное. Вы устанавливаете его в true, когда начинаете загрузку, и устанавливаете его в false, когда заканчиваете.

Затем вы привязываете триггер или визуальное состояние к этому свойству в представлении и используете представление для описания того, как представить пользователю, что данные загружаются.

Этот подход сохраняет разделение, когда модель представления является логическим представлением пользовательского представления, и ей не нужно участвовать в фактическом отображении - или иметь знания об анимации, визуальных состояниях и т.д.

Чтобы использовать DataStateBehavior для изменения визуальных состояний на основе привязки в Silverlight:

<TheThingYouWantToModify ...>
 <i:Interaction.Behaviors>
   <ei:DataStateBehavior Binding="{Binding IsLoadingData}" Value="true" TrueState="HistoryLoading" FalseState="HistoryLoaded" />
 </i:Interaction.Behaviors>
</TheThingYouWantToModify >
12
ответ дан 4 December 2019 в 10:03
поделиться

В прошлом я делал так: объявлял событие в своей виртуальной машине, на которое подписывается View. Затем, когда я хочу указать, что индикатор занятости должен исчезнуть, я поднимаю событие внутри VM.

2
ответ дан 4 December 2019 в 10:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: