1

В WPF у меня есть ListBox со списком, состоящим из UserControls. Элементы управления предназначены для перехода на различные экраны приложения. Каждый UserControl (называемый NavigationButton) имеет значок и текст. Значки в основном представляют собой комбинации нескольких объектов Path, поэтому каждый значок является собственным UserControl, и они отображаются с помощью ContentPresenter. Я хочу иметь возможность анимировать цвет значка в зависимости от разных состояний экрана, но попробовал множество опций и не смог этого сделать.Animate DependencyProperty в UserControl, отображаемом через ContentPresenter

Вот урезанная версия NavigationButton:

<DockPanel Margin="12,0,12,0"> 

     <!-- Icon --> 
     <ContentPresenter x:Name="Content_Icon" Content="{Binding}" Width="20"/> 

     <!-- Text --> 
     <Grid Margin="9,0,0,0"> 
      <TextBlock x:Name="TextBlock_Text" Text="{Binding ScreenName, Converter={StaticResource StringToStringUpperConverter}}" VerticalAlignment="Center" 
         FontSize="15" Foreground="#FFF2F2F2" /> 
     </Grid> 

В принципе, мне нужно анимировать свойство на ContentPresenter, но не знаю, как получить к нему доступ.

Вот ListBox хостинг в NavigationButtons:

 <ListBox DockPanel.Dock="Top" ItemsSource="{Binding ScreenViewModels}" 
      SelectedItem="{Binding SelectedScreenViewModel}"> 

     <ListBox.ItemTemplate> 
      <DataTemplate> 
       <my:NavigationButton/> 
      </DataTemplate> 
     </ListBox.ItemTemplate> 

Я создал базовый UserControl (так называемый IconBaseControl), что все эти иконы UserConrols может унаследовать. У базового элемента управления есть функция DependencyProperty Brush, называемая IconFill. Части дорожек на иконке, которая может изменить связаны с этим свойством:

<Path Data="<data>" Fill="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type my:IconBaseControl}}, Path=IconFill}" 

Я знаю, привязка работает правильно, потому что цвета изменяются при изменении цвета по умолчанию на UserControl. В идеале я хочу использовать VisualStateManager, потому что будет много разных состояний. Итак, у меня есть VisualStateManager в NavigationButton, UserControl, содержащий ContentPresenter, на котором размещается значок (все UserControls, которые наследуют IconBaseControl), называемый Content_Icon. Я пытался что-то подобное в одном из состояний:

<VisualState x:Name="Deselected"> 
      <Storyboard> 

       <ColorAnimation Storyboard.TargetName="TextBlock_Text" Storyboard.TargetProperty="Foreground.Color" 
         To="#FF5e5e5e" Duration="0"/> 

       <ColorAnimation Storyboard.TargetName="Content_Icon" Storyboard.TargetProperty="IconFill" 
         To="#FF5e5e5e" Duration="0"/> 

      </Storyboard> 
</VisualState> 

Но я получаю следующее сообщение об ошибке:

InvalidOperationException: Не удается разрешить все ссылки на недвижимость в пути свойства «IconFill». Убедитесь, что применимые объекты поддерживают свойства.

Я также попытался связывание свойства раскадровки с чем-то вроде этого:

Storyboard.TargetProperty="(IconBaseControl.IconFill) 

Но получить эту ошибку:

IconBaseControl не поддерживается в проекте Windows Presentation Foundation (WPF).

Я также пытался испортить код позади, но не могу понять, как преобразовать ContentPresenter в IconBaseControl. Я понял, что свойство ContentTemplate будет способом, но это ничего.

Любые предложения о том, как оживить это свойство? Открыто для почти чего угодно :) Я кодирую в VB.Net, но любые предложения C# тоже хороши.

Заранее спасибо.

EDIT: Включены коды NavigationButton

ответ

1

Я считаю, что создание подклассы элементов управления WPF могут запутаться и не являются необходимым, если это не очень продвинутой проблема. По-моему, создание сценария IconBaseControl в качестве дочернего элемента UserControl в вашем сценарии слишком велико.

Вот мое предположение, если вы используете MVVM: создайте IconBaseControl как обычный UserControl. Просто создайте IconControl.xaml с кодом IconControl.xaml.cs за файлом, как и любой другой вид.

Вот пример того, что вы бы внутри IconControl:

<UserControl> 
    <Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition /> 
     <ColumnDefinition /> 
    </Grid.ColumnDefinitions> 
    <Grid.Style> 
     <Style> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding IsSelected}" Value="True"> 
        <DataTrigger.EnterActions> 
         <BeginStoryboard> 
          <Storyboard> 
           <ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="#FF5e5e5e" Duration="0:0:0" /> 
          </Storyboard> 
         </BeginStoryboard> 
        </DataTrigger.EnterActions> 
        <DataTrigger.ExitActions> 
         <BeginStoryboard> 
          <Storyboard> 
           <ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="White" Duration="0:0:0" /> 
          </Storyboard> 
         </BeginStoryboard> 
        </DataTrigger.ExitActions> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </Grid.Style> 

    <Image Source="Icon.jpeg" /> 

    <TextBlock Text="{Binding PageName}" Grid.Column="1" /> 

</Grid> 
</UserControl>` 

Обратите внимание, что на фоне окружающей сетки будет изменяться в зависимости от привязки к значению называется IsSelected на DataContext. Итак, на этом этапе вам нужно создать ViewModel под названием IconControlViewModel.cs, который имеет IsSelected boolean, отображаемый как свойство зависимостей.

Наконец вид, который содержит эти кнопки навигации:

<UserControl> 

    <ItemsControl ItemsSource="{Binding ListOf_IconControlViewModels}"> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate DataType="{x:Type IconControlViewModel}"> 
       <local:IconView /> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 

</UserControl> 

Обратите внимание на DataTemplate, который рассказывает ItemsControl, что делают, когда он видит IconControlViewModel в списке ItemsSource. Вот как я мог бы создать его с использованием шаблона MVVM. Надеюсь, это поможет и сообщит мне, если вам нужно разъяснить мой ответ, или это далеко.

Приветствия, Eric

+0

Спасибо за ответ! Единственная проблема заключается в том, что я использую ContentPresenter в элементе управления для отображения другого значка в зависимости от DataContext элемента управления. Список содержит привязку к списку режимов просмотра, каждый из которых представляет собой другой экран. Поэтому каждый экран должен иметь свой собственный значок. Вот в чем проблема: у меня возникают трудности с доступом к любым свойствам ContentPresenter. Вот почему я попытался сделать их одинаковыми, используя наследование, но это ничуть не вызвало меня. – Riggs

+0

Мне сложно визуализировать ваш дизайн, но иногда использование конвертеров полезно при необходимости внесения сложных изменений в пользовательский интерфейс, например значок. Итак, что вы можете сделать, это привязать источник изображения к значению в ViewModel (будет работать enum). И затем используйте конвертер на этом привязке, который принимает это значение перечисления и возвращает правильный значок. Если вы хотите добавить больше кода, я мог бы помочь больше. – baueric

+0

Или у вас может быть ссылка на источник изображения следующим образом: Source = "{Binding Converter = {StaticResource MyIconConverter}}". Что это будет сделано, так это передать весь DataContext в конвертер. Затем вы можете просто вернуть правильный значок в зависимости от типа класса DataContext. – baueric