РадиоБуттон с пользовательским изменением изображения и текста во время выполнения

#c# #wpf #xaml #custom-controls #controltemplate

Вопрос:

У меня есть собственный RadioButton стиль с Image буквой и 2 TextBlock с.

 <Style x:Key="ToggleButton_Chose" TargetType="{x:Type ToggleButton}" >
    
    <Setter Property="Background" Value="#32353B" />
    <Setter Property="Margin" Value="10,5"/>
    <Setter Property="Height" Value="45" />
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="Padding" Value="1" />
    <Setter Property="HorizontalAlignment" Value="Stretch"/>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <StackPanel>
                    <Border BorderBrush="{TemplateBinding BorderBrush}" 
                            Background="{TemplateBinding Background}">
                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0">
                            <ContentPresenter HorizontalAlignment="Left"                  
                                          VerticalAlignment="Center"/>

                            <Image Source="{TemplateBinding Button.Tag}"  HorizontalAlignment="Left" Stretch="Uniform" Width="45" IsEnabled="True" />

                            <StackPanel Margin="2">
                                <TextBlock Foreground="#DCDDDE"  FontSize="18" FontFamily="Arial">Strona</TextBlock>
                                <TextBlock Foreground="#52555C"  FontSize="12" FontFamily="Arial"> Login</TextBlock>
                            </StackPanel>
                        </StackPanel>
                    </Border>
                </StackPanel>

            </ControlTemplate>

        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsChecked" Value="True">
            <Setter Property="Background" Value="#282B2E"/>
        </Trigger>
  
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background">
                <Setter.Value>
                    <SolidColorBrush Color="#FF282B2E" Opacity="0.5"/>
                </Setter.Value>
            </Setter>
        </Trigger>
      
    </Style.Triggers>
</Style>
 

Когда я создаю новую кнопку во время выполнения, устанавливая Style и, я хочу изменить Image и текст для каждого RadioButton из них . Прямо сейчас я подумываю о том, чтобы использовать Tag для этого букву А.

 RadioButton radioButton = new RadioButton();

 radioButton.GroupName = "Side";

 radioButton.Style = (Style)Resources["ToggleButton_Chose"];

 radioButton.Tag = new BitmapImage(new Uri("https://www.google.com/favicon.ico"));
 

Есть ли какой-либо другой способ установить это?
У меня будет около 100 RadioButton s, и любой из них должен получить разные изображения и тексты.

Ответ №1:

Пользовательский Контроль

Если вы хотите создать ToggleButton элемент, требующий дополнительных (привязываемых) свойств, вы можете создать пользовательский элемент управления со свойствами зависимостей для изображения и текстов в зависимости от ToggleButton типа. Создайте новый тип AdvancedToggleButton , производный от ToggleButton .

 public class AdvancedToggleButton : ToggleButton
{
   static AdvancedToggleButton()
   {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(AdvancedToggleButton), new FrameworkPropertyMetadata(typeof(AdvancedToggleButton)));
   }

   public ImageSource ImageSource
   {
      get => (ImageSource) GetValue(ImageSourceProperty);
      set => SetValue(ImageSourceProperty, value);
   }

   public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register(
      nameof(ImageSource), typeof(ImageSource), typeof(AdvancedToggleButton));

   public string FirstText
   {
      get => (string) GetValue(FirstTextProperty);
      set => SetValue(FirstTextProperty, value);
   }

   public static readonly DependencyProperty FirstTextProperty = DependencyProperty.Register(
      nameof(FirstText), typeof(string), typeof(AdvancedToggleButton));

   public string SecondText
   {
      get => (string) GetValue(SecondTextProperty);
      set => SetValue(SecondTextProperty, value);
   }

   public static readonly DependencyProperty SecondTextProperty = DependencyProperty.Register(
      nameof(SecondText), typeof(string), typeof(AdvancedToggleButton));
}
 

Затем вы можете создать неявный стиль по умолчанию (опустив x:Key ) в ресурсах приложения или другом словаре ресурсов в области действия, чтобы стиль применялся автоматически.

 <Style TargetType="{x:Type local:AdvancedToggleButton}">

   <Setter Property="Background"
           Value="#32353B" />
   <Setter Property="Margin"
           Value="10,5" />
   <Setter Property="Height"
           Value="45" />
   <Setter Property="BorderThickness"
           Value="0" />
   <Setter Property="Padding"
           Value="1" />
   <Setter Property="HorizontalAlignment"
           Value="Stretch" />

   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type local:AdvancedToggleButton}">
            <StackPanel>
               <Border BorderBrush="{TemplateBinding BorderBrush}"
                       Background="{TemplateBinding Background}">
                  <StackPanel Orientation="Horizontal"
                              HorizontalAlignment="Left"
                              Margin="0">
                     <ContentPresenter HorizontalAlignment="Left"
                                       VerticalAlignment="Center" />

                     <Image Source="{TemplateBinding ImageSource}"
                            HorizontalAlignment="Left"
                            Stretch="Uniform"
                            Width="45"
                            IsEnabled="True" />

                     <StackPanel Margin="2">
                        <TextBlock Foreground="#DCDDDE"
                                   FontSize="18"
                                   FontFamily="Arial"
                                   Text="{TemplateBinding FirstText}"/>
                        <TextBlock Foreground="#52555C"
                                   FontSize="12"
                                   FontFamily="Arial"
                                   Text="{TemplateBinding SecondText}"/>
                     </StackPanel>
                  </StackPanel>
               </Border>
            </StackPanel>

         </ControlTemplate>

      </Setter.Value>
   </Setter>
   <Style.Triggers>
      <Trigger Property="IsChecked"
               Value="True">
         <Setter Property="Background"
                 Value="#282B2E" />
      </Trigger>

      <Trigger Property="IsMouseOver"
               Value="True">
         <Setter Property="Background">
            <Setter.Value>
               <SolidColorBrush Color="#FF282B2E"
                                Opacity="0.5" />
            </Setter.Value>
         </Setter>
      </Trigger>

   </Style.Triggers>
</Style>
 

Обратите внимание, что я удалил дубликат сеттера для HorizontalAlignment . Image Привязывает его Source к ImageSource свойству, а TextBlock s привязывает к FirstText и SecondText соответственно. Вы можете либо определить AdvancedToggleButton его в XAML, либо в коде.

 <local:AdvancedToggleButton ImageSource="Resources/Check.jpg"
                            FirstText="Strona"
                            SecondText="Login"/>
 
 RadioButton radioButton = new RadioButton();
radioButton.GroupName = "Side";
radioButton.ImageSource = new BitmapImage(new Uri("https://www.google.com/favicon.ico"));
radioButton.FirstText = "Strona";
radioButton.SecondText = "Login"
 

Полезные ресурсы по пользовательским элементам управления:

Присоединенные Свойства

Альтернативой без создания пользовательского элемента управления является создание набора присоединенных свойств.

 public static class ToggleButtonProperties
{
   public static ImageSource GetImageSource(DependencyObject dependencyObject)
   {
      return (ImageSource) dependencyObject.GetValue(ImageSourceProperty);
   }

   public static void SetImageSource(DependencyObject dependencyObject, ImageSource value)
   {
      dependencyObject.SetValue(ImageSourceProperty, value);
   }

   public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.RegisterAttached(
      "ImageSource", typeof(ImageSource), typeof(ToggleButtonProperties));

   public static string GetFirstText(DependencyObject dependencyObject)
   {
      return (string) dependencyObject.GetValue(FirstTextProperty);
   }

   public static void SetFirstText(DependencyObject dependencyObject, string value)
   {
      dependencyObject.SetValue(FirstTextProperty, value);
   }

   public static readonly DependencyProperty FirstTextProperty = DependencyProperty.RegisterAttached(
      "FirstText", typeof(string), typeof(ToggleButtonProperties));

   public static string GetSecondText(DependencyObject dependencyObject)
   {
      return (string) dependencyObject.GetValue(SecondTextProperty);
   }

   public static void SetSecondText(DependencyObject dependencyObject, string value)
   {
      dependencyObject.SetValue(SecondTextProperty, value);
   }

   public static readonly DependencyProperty SecondTextProperty = DependencyProperty.RegisterAttached(
      "SecondText", typeof(string), typeof(ToggleButtonProperties));
}
 

Эти свойства могут быть связаны в шаблоне элемента управления с помощью круглых скобок, что является синтаксисом привязки для присоединенных свойств и RelativeSource к родительскому ToggleButton элементу .

 <Style x:Key="ToggleButton_Chose" TargetType="{x:Type ToggleButton}">

   <Setter Property="Background"
           Value="#32353B" />
   <Setter Property="Margin"
           Value="10,5" />
   <Setter Property="Height"
           Value="45" />
   <Setter Property="BorderThickness"
           Value="0" />
   <Setter Property="Padding"
           Value="1" />
   <Setter Property="HorizontalAlignment"
           Value="Stretch" />

   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type ToggleButton}">
            <StackPanel>
               <Border BorderBrush="{TemplateBinding BorderBrush}"
                       Background="{TemplateBinding Background}">
                  <StackPanel Orientation="Horizontal"
                              HorizontalAlignment="Left"
                              Margin="0">
                     <ContentPresenter HorizontalAlignment="Left"
                                       VerticalAlignment="Center" />

                     <Image Source="{Binding (local:ToggleButtonProperties.ImageSource), RelativeSource={RelativeSource TemplatedParent}}"
                            HorizontalAlignment="Left"
                            Stretch="Uniform"
                            Width="45"
                            IsEnabled="True" />

                     <StackPanel Margin="2">
                        <TextBlock Foreground="#DCDDDE"
                                   FontSize="18"
                                   FontFamily="Arial"
                                   Text="{Binding (local:ToggleButtonProperties.FirstText), RelativeSource={RelativeSource TemplatedParent}}"/>
                        <TextBlock Foreground="#52555C"
                                   FontSize="12"
                                   FontFamily="Arial"
                                   Text="{Binding (local:ToggleButtonProperties.SecondText), RelativeSource={RelativeSource TemplatedParent}}"/>
                     </StackPanel>
                  </StackPanel>
               </Border>
            </StackPanel>

         </ControlTemplate>

      </Setter.Value>
   </Setter>
   <Style.Triggers>
      <Trigger Property="IsChecked"
               Value="True">
         <Setter Property="Background"
                 Value="#282B2E" />
      </Trigger>

      <Trigger Property="IsMouseOver"
               Value="True">
         <Setter Property="Background">
            <Setter.Value>
               <SolidColorBrush Color="#FF282B2E"
                                Opacity="0.5" />
            </Setter.Value>
         </Setter>
      </Trigger>

   </Style.Triggers>
</Style>
 

Назначение или привязка присоединенных свойств выполняется с помощью статического класса.

 <ToggleButton Style="{StaticResource ToggleButton_Chose}"
              local:ToggleButtonProperties.ImageSource="Resources/Check.jpg"
              local:ToggleButtonProperties.FirstText="Strona"
              local:ToggleButtonProperties.SecondText="Login"/>
 
 RadioButton radioButton = new RadioButton();
radioButton.GroupName = "Side";
ToggleButtonProperties.SetImageSource(radioButton, new BitmapImage(new Uri("https://www.google.com/favicon.ico")));
ToggleButtonProperties.SetFirstText(radioButton, "Strona");
ToggleButtonProperties.SetSecondText(radioButton, "Login");
 

Полезные ресурсы по вложенным свойствам:

Ответ №2:

Но есть ли какой-нибудь другой способ установить это? У меня будет около 100 радиокнопок, и любая из них должна получить другое изображение и текст.

Создайте простой тип данных:

     public class ButtonContent
    {
        public string Strona { get; set; }
        public string Login { get; set; }
        public object ImageSource { get; set; }
    }
 

Этот тип будет использоваться для передачи данных кнопки в контекст.
Поэтому мы немного изменим стиль:

         <Style x:Key="ToggleButton_Chose" TargetType="{x:Type ToggleButton}" >

            <Setter Property="Background" Value="#32353B" />
            <Setter Property="Margin" Value="10,5"/>
            <Setter Property="Height" Value="45" />
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="HorizontalAlignment" Value="Left" />
            <Setter Property="Padding" Value="1" />
            <Setter Property="HorizontalAlignment" Value="Stretch"/>

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                        <StackPanel>
                            <Border BorderBrush="{TemplateBinding BorderBrush}" 
                            Background="{TemplateBinding Background}">
                                <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0">
                                    <ContentPresenter HorizontalAlignment="Left"                  
                                          VerticalAlignment="Center"/>

                                    <Image Source="{Binding ImageSource}"
                                           HorizontalAlignment="Left" Stretch="Uniform" Width="45" IsEnabled="True" />

                                    <StackPanel Margin="2">
                                        <TextBlock Foreground="#DCDDDE"  FontSize="18" FontFamily="Arial"
                                                   Text="{Binding Strona}"/>
                                        <TextBlock Foreground="#52555C"  FontSize="12" FontFamily="Arial"
                                                   Text="{Binding Login}"/>
                                    </StackPanel>
                                </StackPanel>
                            </Border>
                        </StackPanel>

                    </ControlTemplate>

                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Background" Value="#282B2E"/>
                </Trigger>

                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background">
                        <Setter.Value>
                            <SolidColorBrush Color="#FF282B2E" Opacity="0.5"/>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
 

Для набора кнопок используйте ItemsControl:

         <DataTemplate x:Key="itemTemplate" DataType="{x:Type local:ButtonContent}">
            <ToggleButton Style="{DynamicResource ToggleButton_Chose}"/>
        </DataTemplate>
 
         <ItemsControl x:Name="itemsControl"
                      ItemTemplate="{DynamicResource itemTemplate}">
        </ItemsControl>
 

Создайте наблюдаемую коллекцию с данными и передайте ее в источник ItemsControl:

     public partial class MainWindow : Window
    {
        private readonly ObservableCollection<ButtonContent> ButtonContents
            = new ObservableCollection<ButtonContent>();
        public MainWindow()
        {
            InitializeComponent();
            itemsControl.ItemsSource = ButtonContents;
            ButtonContents.Add(new ButtonContent() { Strona = "Strona1", Login = "Login1", ImageSource = "Image/block.png" });
            ButtonContents.Add(new ButtonContent() { Strona = "Strona2", Login = "Login2", ImageSource = "Image/block.png" });
            ButtonContents.Add(new ButtonContent() { Strona = "Strona3", Login = "Login3", ImageSource = "Image/block.png" });
            ButtonContents.Add(new ButtonContent() { Strona = "Strona4", Login = "Login4", ImageSource = "Image/block.png" });
        }

    }