Ответы пользователя по тегу WPF
  • Можно ли как-то получить доступ к отображающим элементам ToolBar?

    WNeZRoS
    @WNeZRoS
    требуется подсвечивать текущий элемент

    с помощью события нажания


    Лучше не делать таких не явных предположений о текущем элементе, кнопка может быть нажата, но из-за каких-то внутренних условий "текущесть" может не измениться.
    Лучше завести во вью модели флаг о том что он текущий и по нему подсвечивать. Проще всего в той же, для которой ItemTemplate с кнопкой написан.

    Но если хочется более чистого кода, то можно через RelativeSource вылезти во внешнюю вью модель и сделать проверку через мультибининг с конвертером
    <MultiBinding Converter="{StaticResource IsEqualsConverter}">
        <Binding />
        <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ToolBar}}" Path="DataContext.CurrentElement" />
    </MultiBinding>


    Если вдруг под подсветить вы имеете в виде не что-то своё хитрое, а фокус, то можно использовать экстеншен с attached dependency property для установки фокуса. В этом случае для определения первого элемента поможет {RelativeSource Mode=PreviousData} - у первого элемента он будет {x:Null}
    Ответ написан
    Комментировать
  • Раскрывающийся список с объектами в WPF?

    WNeZRoS
    @WNeZRoS
    То что на картинке выглядит как Expander, в Header у него панель с иконкой и текстом, в Content - DataGrid с таблицей. Если таких экспандеров не фиксированное кол-во, то их можно генерить с помощью ItemsControl
    Ответ написан
    3 комментария
  • Как в wpf mvvc отобразить список элементов динамически?

    WNeZRoS
    @WNeZRoS
    Со структурой как у вас не понятно как оно должно работать.
    Если сделать ItemData типизированными, то всё становится проще - wpf может по типу выбирать подходящий DataTemplate, и значения мы получаем нужного типа, а не пойми что.

    public class SettingViewHost
    {
        public IReadOnlyCollection<SettingView>? Views { get; init; }
    }
    
    public class SettingView
    {
        public string? Name { get; init; }
        public IReadOnlyCollection<ItemData>? Options { get; init; }
    }
    
    public class ItemData
    {
        public string? Name { get; init; }
        public int GridRow { get; init; }
        public int GridColumn { get; init; }
    }
    
    public class BooleanItemData : ItemData // TODO implement INotifyPropertyChanged and raise PropertyChanged in Value setter
    {
        public bool Value { get; set; }
    }
    
    public class StringItemData : ItemData // TODO implement INotifyPropertyChanged and raise PropertyChanged in Value setter
    {
        public string Value { get; set; } = string.Empty;
    }


    Собираем SettingViewHost с любым количеством SettingView с разными внутренними настройками и закидываем во вью с такими шаблонами:

    <DataTemplate DataType="{x:Type local:StringItemData}">
        <StackPanel Orientation="Vertical">
            <TextBlock Text="{Binding Name, Mode=OneTime}" />
            <TextBox Text="{Binding Value}" />
        </StackPanel>
    </DataTemplate>
    
    <DataTemplate DataType="{x:Type local:BooleanItemData}">
        <CheckBox Content="{Binding Name, Mode=OneTime}" IsChecked="{Binding Value}" />
    </DataTemplate>
    
    <DataTemplate DataType="{x:Type local:SettingView}">
        <ItemsControl ItemsSource="{Binding Options}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid IsItemsHost="True">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                    </Grid>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="{x:Type ContentPresenter}">
                    <Setter Property="Grid.Column" Value="{Binding GridColumn, Mode=OneTime}" />
                    <Setter Property="Grid.Row" Value="{Binding GridRow, Mode=OneTime}" />
                    <Setter Property="Margin" Value="4" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
    </DataTemplate>
    
    <DataTemplate DataType="{x:Type local:SettingViewHost}">
        <StackPanel Orientation="Vertical">
            <ComboBox Name="ComboBox" ItemsSource="{Binding Views}">
                <ComboBox.ItemTemplate>
                    <DataTemplate DataType="{x:Type local:SettingView}">
                        <TextBlock Text="{Binding Name, Mode=OneTime}" />
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
            
            <ContentControl Content="{Binding ElementName=ComboBox, Path=SelectedItem}" />
        </StackPanel>
    </DataTemplate>


    Пример сделан с Grid'ом 2х2, можно использовать любую панель. Обычный Grid для динамических целей не удобен т.к. у него фиксированное кол-во строк и столбцов. Лучше использовать UniformGrid или что-то ещё, или развлекаться с attached dependency property или MarkupExtension чтобы динамически генерировать строки и столбцы.
    Ответ написан
    Комментировать
  • Как в логике mvvc wpf передать в класс ViewModel из view свойство класса .xaml.cs?

    WNeZRoS
    @WNeZRoS
    Почему это поле не передается через, передается null
    CommandParameter="{Binding ElementName=selfUserControl,Path=Points, Mode=OneWay}">, selfUserControl x:Name элемента.

    Проперти Points должен быть public

    Находил примеры, там везде свойство ItemsSource, но такого свойства нету у Canvas

    Надо использовать ItemsControl, у которого как раз есть ItemsSource, а Canvas использовать как панель для этого ItemsControl через ItemsPanel
    Ответ написан
    Комментировать
  • Как индефицировать объект списка по нажатию кнопки в нём?

    WNeZRoS
    @WNeZRoS
    Если используете команды, то надо у кнопки сделать CommandParameter="{Binding}" и тогда вьюмодель строки придёт в команду.
    Если на Click эвентах, то из аргументов эвента достать кнопку и посмотреть на её DataContext.
    Ответ написан
    Комментировать
  • Как разрабатывать ПО с GUI, который адаптируется под определённого пользователя?

    WNeZRoS
    @WNeZRoS
    использовать условные конструкции для решения об отображении или неотображении тех или иных элементов GUI, либо иметь разные XAML-формы для разных групп пользователей


    Это одно и тоже решение, просто условия разное меняют. Всё зависит от вида приложения можно применять разные решения.

    Для хороших WPF приложений принято использовать MVVM, соответственно для всего будут вьюмодели.
    Если это приложение - набор ярлыков, то можно просто не создавать вью модели или скрывать (BooleanToVisibilityConverter) недоступные ярлыки.
    Если что-то сложнее, то вероятно для разных пользователей будут разные вьюмодели и разные XAML шаблоны для них.
    Ответ написан
    Комментировать
  • Как сделать прозрачный фон png фото wpf?

    WNeZRoS
    @WNeZRoS
    Чтобы был прозрачный фон у png в wpf, нужно чтобы у самой png фон был прозрачный, а не серо-белая клеточка.
    Ответ написан
    Комментировать
  • Как сделать чтобы при изменение размера окна кнопки пропорционально изменялись по высоте?

    WNeZRoS
    @WNeZRoS
    StackPanel в Vertical ориентации делает все элементы минимальной высоты.
    Ваш вариант с Grid не работает как нужно потому что Height="*" ставить для второй строки, той которую нужно растянуть.
    Я бы в данном случае использовать DockPanel - в ней (по умолчанию) последний элемент растягивается на весь оставшийся объём.
    Ответ написан
    1 комментарий
  • Как в данном примере автозаполнение, сделать так что бы клавишей "keydown" (стрелочка вниз) переходило по выпадающим вариантам?

    WNeZRoS
    @WNeZRoS
    Самый простой вариант:
    Заменить ScrollViewer на ListBox, и заполнять его элементы вместо StackPanel.
    В эвенте TextBox_KeyUp, если нажата клавиша Down или Up изменять SelectedIndex у ListBox'a
    Вся магия с MouseEnter и MouseLeave будет не нужна. Вместо MouseLeftButtonUp на элементе надо обрабатывать SelectionChanged у ListBox'a
    Ответ написан
    Комментировать
  • Почему я не могу задать цвет кисти GeometryDrawing.Brush, выдает исключение?

    WNeZRoS
    @WNeZRoS
    У вас GeometryDrawing (скорее всего DrawingGroup и DrawingBrush тоже) находятся в "замороженном" состоянии. В этом состоянии в них нельзя ничего менять, и они работают более эффективно. Проверить это можно посмотрев на значение проперти IsFrozen. Лучше всего будет убрать их замораживание, но если это проблематично, то можно сделать редактируемую копию DrawingBrush методом Clone.
    Ответ написан
    Комментировать
  • Как получить текущие размеры элемента без события изменения его размера?

    WNeZRoS
    @WNeZRoS
    Если делать Observe через биндинги, то всё работает сразу же, без SizeChanged эвента.
    private static void OnObserveChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var frameworkElement = (FrameworkElement)dependencyObject;
    
        if ((bool)e.NewValue)
        {
            frameworkElement.SetBinding(ObservedWidthProperty, new Binding(nameof(FrameworkElement.ActualWidth)) { RelativeSource = RelativeSource.Self });
            frameworkElement.SetBinding(ObservedHeightProperty, new Binding(nameof(FrameworkElement.ActualHeight)) { RelativeSource = RelativeSource.Self });
        }
        else
        {
            frameworkElement.ClearValue(ObservedWidthProperty);
            frameworkElement.ClearValue(ObservedHeightProperty);
        }
    }
    Ответ написан
    Комментировать
  • Как изменить фон у button по Click при реализованном новом шаблоне?

    WNeZRoS
    @WNeZRoS
    Чтобы button.Background = Brushes.Red; работал нужно в ControlTemplate для Border'a вместо фиксированного цвета/градиента использовать биндинг на фон кнопки:
    {TemplateBinding Background}

    Но при этом в IsPressed триггере всё ещё останется фиксированный цвет, чтобы его тоже можно было менять нужно найти или создать (attached) DependencyProperty у кнопки куда этот цвет можно забиндить.
    Так же в триггерах не работает TemplateBinding, вместо него следует использовать такой биндинг:
    {Binding RelativeSource={RelativeSource TemplatedParent}, Path=PressedBackground}
    Ответ написан
    1 комментарий
  • C# WPF программное создание кнопки?

    WNeZRoS
    @WNeZRoS
    У GroupBox в WPF нет Children, но есть Content. В Content нужно положить какую-то панель, а уже потом в эту панель (у неё есть Children) можно добавлять элементы.

    <GroupBox Header="" HorizontalAlignment="Left" Height="100" Margin="10,67,0,0" VerticalAlignment="Top" Width="772">
        <StackPanel Name="ConfigsBox" />
    </GroupBox>
    Ответ написан
    Комментировать
  • Как отрисовать много элементов в ItemsControl?

    WNeZRoS
    @WNeZRoS
    Лаг может возникать из-за нескольких причин.
    Первая причниа - долгая загрузка или создание элементов. Тогда, как пишет Михаил Усоцкий, следует вынести загрузку в другой поток.

    Вторая причина - долгая отрисовка в WPF. В данном случае поможет виртуализация. Если у вас используется ItemsControl, то стоит его заменить на ListBox - в ItemsControl не работает виртуализация.
    <ListBox VirtualizingStackPanel.IsVirtualizing="True" ItemsSource="{Binding ...}" />

    Если у вас используется нестандартный ItemsPanel, то нужно также использовать панель с поддержкой виртуализации. В стандартном WPF такая одна - VirtualizingStackPanel, аналог StackPanel. Какие-то другие придётся писать самому или искать уже кем-то написанные.
    Ответ написан
  • Почему SelectedItem у ListView срабатывает только 1 раз, а SelectedIndex - всегда?

    WNeZRoS
    @WNeZRoS
    А что у вас в Items?
    Я такое поведение видел только когда в Items одинаковые элементы (ReferenceEquals(a, b) == true)
    Ответ написан
  • WPF: Как в ToolTip загнать результат статического метода, в который передается значение из модели?

    WNeZRoS
    @WNeZRoS
    Совсем без кода не получится. Нужно сделать IMultiValueConverter, куда биндить строку и число. В самом конвертере вызывать этот статичный метод. При этом TextBlock что возвращается должен быть новым (нигде до этого не использованным), а то будут краши.
    Ответ написан
    Комментировать
  • Как добавить в ListBox кнопку в начало или конец списка?

    WNeZRoS
    @WNeZRoS
    При бидинге коллекции к ItemsControl создаётся CollectionView (для IEnumerable, для IList - ListCollectionView), который может дополнительно отсортировать или отфильтровать коллекцию. Элементы, видимые на экране, берутся из этого CollectionView, а не из оригинальной коллекции.

    ListCollectionView имеет проперти NewItemPlaceholderPosition, с помощью которого можно добавить фейковый элемент в начало или конец.
    // CollectionView можно получить двумя способами:
    // 1. Просто получить дефолтный вариант для коллекции
    var collectionView = (ListCollectionView)CollectionViewSource.GetDefaultView(Items);
    // 2. Создать свой, тогда именно этот созданный collectionView надо передавать для биндинга в ItemsSource.
    var collectionView = new ListCollectionView(Models);
    
    // После, можно добавить фейковый элемент
    collectionView.NewItemPlaceholderPosition = NewItemPlaceholderPosition.AtBeginning;

    После этого в ListBox появится элемент CollectionView.NewItemPlaceholder, который можно отдельно стилизовать с помощью DataTemplateSelector.

    DataTemplateSelector
    public sealed class NewItemTemplateSelector : DataTemplateSelector
    {
       public DataTemplate ItemTemplate { get; set; }
       public DataTemplate NewItemPlaceholderTemplate { get; set; }
    
       public override DataTemplate SelectTemplate(object item, DependencyObject container)
       {
          if (item == CollectionView.NewItemPlaceholder)
             return NewItemPlaceholderTemplate;
    
          return ItemTemplate;
       }
    }

    <ListBox.ItemTemplateSelector>
        <t:NewItemTemplateSelector>
            <t:NewItemTemplateSelector.ItemTemplate>
                <DataTemplate>
                    <!-- Шаблон обычного элемента -->
                </DataTemplate>
            </t:NewItemTemplateSelector.ItemTemplate>
            <t:NewItemTemplateSelector.NewItemPlaceholderTemplate>
                <DataTemplate>
                    <!-- Шаблон CollectionView.NewItemPlaceholder -->
                    <Button Content="+" />
                </DataTemplate>
            </t:NewItemTemplateSelector.NewItemPlaceholderTemplate>
        </t:NewItemTemplateSelector>
    </ListBox.ItemTemplateSelector>

    Ответ написан
    Комментировать
  • Как быть, если при наведении курсора на кнопку пропадает background?

    WNeZRoS
    @WNeZRoS
    Надо написать свой Style/Template для кнопок который не будет так делать и, возможно, как-то по другому отображать наведение.
    Ответ написан
    Комментировать
  • Удаление строки в DataGrid?

    WNeZRoS
    @WNeZRoS
    Если List заменить на ObservableCollection, то при удалении из коллекции строка удалиться и из DataGrid'a.
    Или другой (плохой) вариант. После удаления из списка вызывать dg.Items.Refresh(), но это вызовет обновление всех строк.
    Ответ написан
    3 комментария