Как заполнить button grid'ом из кода и заполнить его textblock'ами?

221
12 июля 2018, 17:00

У меня создаются кнопки в окне cs приложения WPF. Я хочу взять одну из кнопок и заполнить ее одной буквой, но чтобы в правом верхнем еще был маленький значок *3. Для этого заполняю кнопку grid и в этот grid помещаю 2 текстблока соответственно с строками столбцами

Grid _gridforbutton = new Grid();
        TextBlock _double = new TextBlock();
        _double.Text = "*3";
        _double.Foreground = Brushes.Red;
        _double.FontSize = 5;

        TextBlock _word = new TextBlock();
        _word.Text = letter;
        _word.TextAlignment = TextAlignment.Center;
        _word.FontSize = 30;
        _gridforbutton.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(5) });
        _gridforbutton.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(25) });
        _gridforbutton.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(5) });
        _gridforbutton.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(25) });
        _gridforbutton.Children.Add(_double);
        Grid.SetRow(_double, 0);
        Grid.SetColumn(_double, 1);
        _gridforbutton.Children.Add(_word);
        Grid.SetRowSpan(_word, 2);
        Grid.SetColumnSpan(_word, 2);
        btn1.Content = _gridforbutton;

Проблема в том, что я хочу сделать так, будто буква заполняет всю кнопку, а значок *3 в правом крайнем углу как будто добавлен. Но в итоге *3 располагается сверху а кнопку почему-то не видно полностью

Answer 1

И так, посмотрим на вашу задачу, что требуется? А требуется нам некая кнопка, которая будет иметь дополнительные значения, иметь свой стиль и все это должно быть удобно! Для таких целей можно использовать UserControl. Давайте сделаем подобный:

  • Жмем по нашему проекту правой кнопкой мыши - Добавить - Пользовательский элемент управления - Думаем над именем и жмем Ок.
  • Далее у нас будет почти все тоже самое, что и у обычного окна (.xaml файл и рядом .cs файл).
  • В .cs файл нам надо задать DependencyProperty на все доп свойства нашего контрола, пишем и получаем что то вроде этого:

    public partial class CustomButton : UserControl
    {
        public CustomButton()
        {
            InitializeComponent();
        }
        public Visibility DoubleVisibility
        {
            get => (Visibility)GetValue(DoubleVisibilityProperty);
            set => SetValue(DoubleVisibilityProperty, value);
        }
        public Brush DoubleBackground
        {
            get => (Brush)GetValue(DoubleBackgroundProperty);
            set => SetValue(DoubleBackgroundProperty, value);
        }
        public Brush DoubleForeground
        {
            get => (Brush)GetValue(DoubleForegroundProperty);
            set => SetValue(DoubleForegroundProperty, value);
        }
        public string DoubleText
        {
            get => (string)GetValue(DoubleTextProperty);
            set => SetValue(DoubleTextProperty, value);
        }
        public string Text
        {
            get => (string)GetValue(TextProperty);
            set => SetValue(TextProperty, value);
        }
        public ICommand ButtonCommand
        {
            get => (ICommand)GetValue(ButtonCommandProperty);
            set => SetValue(ButtonCommandProperty, value);
        }
        public static readonly DependencyProperty DoubleVisibilityProperty =
            DependencyProperty.Register("DoubleVisibility", typeof(Visibility), typeof(CustomButton), new PropertyMetadata(Visibility.Visible));
        public static readonly DependencyProperty DoubleBackgroundProperty =
            DependencyProperty.Register("DoubleBackground", typeof(Brush), typeof(CustomButton), new PropertyMetadata(new SolidColorBrush(Color.FromArgb(255, 252, 74, 38))));
        public static readonly DependencyProperty DoubleForegroundProperty =
            DependencyProperty.Register("DoubleForeground", typeof(Brush), typeof(CustomButton), new PropertyMetadata(Brushes.White));
        public static readonly DependencyProperty DoubleTextProperty =
            DependencyProperty.Register("DoubleText", typeof(string), typeof(CustomButton), new PropertyMetadata("0"));
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(CustomButton), new PropertyMetadata("Text"));
        public static readonly DependencyProperty ButtonCommandProperty =
            DependencyProperty.Register("ButtonCommand", typeof(ICommand), typeof(CustomButton), new PropertyMetadata(null));
    }
    
  • Отлично! Теперь нужно сделать вид нашего контрола. Так, как нам требуется кнопка, то давайте перепишем ей дизайн:

    <Button Command="{Binding ButtonCommand, ElementName=Control}" CommandParameter="{Binding}">
        <Button.Template>
            <ControlTemplate TargetType="{x:Type Button}">
                <Grid>
                    <Grid.Effect>
                        <DropShadowEffect BlurRadius="4" ShadowDepth="0" Direction="-90" Opacity="0.5"/>
                    </Grid.Effect>
                    <Border Name="Mask" Background="White" CornerRadius="7" Margin="5"/>
                    <Border Background="White" Margin="5">
                        <Border.OpacityMask>
                            <VisualBrush Visual="{Binding ElementName=Mask}"/>
                        </Border.OpacityMask>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition Width="2*"/>
                            </Grid.ColumnDefinitions>
                            <Grid Visibility="{Binding DoubleVisibility, ElementName=Control, FallbackValue=Visible, TargetNullValue=Visible}">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="2*"/>
                                </Grid.RowDefinitions>
                                <Grid>
                                    <Polygon Points="100,0, 0,100 0,0"
                                                 Fill="{Binding DoubleBackground, ElementName=Control, FallbackValue=#FC4A26, TargetNullValue=#FC4A26}"
                                                 Stretch="Uniform"
                                                 HorizontalAlignment="Left" VerticalAlignment="Top"/>
                                    <Viewbox HorizontalAlignment="Left" VerticalAlignment="Top">
                                        <TextBlock Padding="5,5,25,0"
                                                       Text="{Binding DoubleText, ElementName=Control, StringFormat=x{0}, FallbackValue=x0, TargetNullValue=x0}" 
                                                       Foreground="{Binding DoubleForeground, ElementName=Control, FallbackValue=White, TargetNullValue=White}"/>
                                    </Viewbox>
                                </Grid>
                            </Grid>
                            <Viewbox Grid.ColumnSpan="2" Grid.Column="0">
                                <TextBlock Margin="10" Foreground="#4d4d4d"
                                                       Text="{Binding Text, ElementName=Control, FallbackValue=Text, TargetNullValue=Text}" 
                                                       HorizontalAlignment="Center"
                                                       VerticalAlignment="Center"/>
                            </Viewbox>
                        </Grid>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Button.Template>
    </Button>
    
  • Самому UserControl задаем свойством его имя:

    Name="Control"
    
  • Ну что, это уже как минимум втрое облегчит вашу задачу (а может даже и решит ее), ведь у вас уже готовый контрол и вместо редактирования кнопки через код, вы можете сразу вызвать этот самый контрол, задав ему все необходимые значения.

Но! Я не устану повторять, что создавать объекты через код не верный подход! WPF это не WinForms, тут есть очень замечательная вещь - Binding. А отличным помощником для этих всех привязок является MVVM паттерн, а у него есть очень забавное правило - "Код не должен знать о View (xaml разметке)".

Давайте я покажу простейшую реализацию MVVM, где мы будет выводить несколько созданных нами кнопок:

  • Нам потребуется пару классов для команд:

    public class RelayCommand<T> : ICommand
    {
        private Action<T> action;
        public RelayCommand(Action<T> action) => this.action = action;
        public bool CanExecute(object parameter) => true;
        #pragma warning disable CS0067
        public event EventHandler CanExecuteChanged;
        #pragma warning restore CS0067
        public void Execute(object parameter) => action((T)parameter);
    }
    public class RelayCommand : ICommand
    {
        private Action action;
        public RelayCommand(Action action) => this.action = action;
        public bool CanExecute(object parameter) => true;
        #pragma warning disable CS0067
        public event EventHandler CanExecuteChanged;
        #pragma warning restore CS0067
        public void Execute(object parameter) => action();
    }
    
  • Далее нам нужен класс, который будет описывать свойства нашей кнопки:

    public class ButtonViewModel
    {
        public Visibility DoubleVisibility { get; set; }
        public Brush DoubleBackground { get; set; }
        public Brush DoubleForeground { get; set; }
        public string DoubleText { get; set; }
        public string Text { get; set; }
        public ICommand Command { get; set; }
    }
    
  • Теперь нам нужно сделать основной класс, который будет задан как DataContext, некий MainViewModel. В нем создадим коллекцию, заполним ее и сделаем обработчик наших кнопок:

    public class MainViewModel
    {
        public ObservableCollection<ButtonViewModel> Buttons { get; set; } = new ObservableCollection<ButtonViewModel>();
        public MainViewModel()
        {
            Buttons.Add(new ButtonViewModel
            {
                Text = "A",
                DoubleText = "2",
                DoubleVisibility = Visibility.Visible,
                DoubleBackground = Brushes.Green,
                Command = new RelayCommand<ButtonViewModel>(Clicked)
            });
            Buttons.Add(new ButtonViewModel
            {
                Text = "B",
                DoubleText = "10",
                DoubleVisibility = Visibility.Visible,
                Command = new RelayCommand<ButtonViewModel>(Clicked)
            });
            Buttons.Add(new ButtonViewModel
            {
                Text = "C",
                DoubleVisibility = Visibility.Collapsed,
                Command = new RelayCommand<ButtonViewModel>(Clicked)
            });
        }
    
        void Clicked(ButtonViewModel obj)
        {
            Console.WriteLine($"Кнопка {obj.Text} нажата!");
        }
    }
    
  • Привяжем все это дело:

    private MainViewModel MainViewModel { get; } = new MainViewModel();
    public MainWindow()
    {
        InitializeComponent();
        DataContext = MainViewModel;
    }
    
  • Ну и последнее, создаем View для этого всего. Для этого классно подходит ItemsControl:

    <ItemsControl ItemsSource="{Binding Buttons}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <local:CustomButton Width="150" Height="150"
                                    ButtonCommand="{Binding Command}" 
                                    Text="{Binding Text}" 
                                    DoubleVisibility="{Binding DoubleVisibility}"
                                    DoubleBackground="{Binding DoubleBackground}"
                                    DoubleForeground="{Binding DoubleForeground}"
                                    DoubleText="{Binding DoubleText}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    
  • Фух, ну все, время запускать и любоваться результатом:

Ну, на этом все, надеюсь я показал вам правильную работу с элементами в WPF. Удачного изучения!

READ ALSO
Имя типа или пространства имен &ldquo;ServiceModel&rdquo; отсутствует в пространстве имен &ldquo;System&rdquo;

Имя типа или пространства имен “ServiceModel” отсутствует в пространстве имен “System”

Пытаюсь подключить CEFПри компиляции возникает вышеуказанная ошибка

155
Как совместить модули с разными ЯП?

Как совместить модули с разными ЯП?

Например: На c++ программа циклически считывает данные ожидая определенного событияКак только это событие произойдет должно высветиться...

180
Как проверить, что список содержит слово?

Как проверить, что список содержит слово?

У меня есть списокВ нем элементы вида: "_something":"",

158
Имплементация Producer/Consumer pattern

Имплементация Producer/Consumer pattern

Паттерн producer/consumer достаточно часто встречается в многопоточном программированииЕго смысл состоит в том, что один или несколько потоков...

179