Ответы пользователя по тегу WPF
  • Как выполнить команду по событию KeyDown для TextBox?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно использовать конвертер i:InvokeCommandAction из System.Windows.Interactivity.

    1) Добавить сборку System.Windows.Interactivity с помощью добавления ссылки (она в списке расширений).
    5cf0c5efb3bee685207836.png
    2) Добавить указанную сборку в xaml:
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

    3) Использовать i:EventTrigger и i:InvokeCommandAction:
    <TextBox>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="KeyDown">
                <i:InvokeCommandAction Command="{Binding KeyDownCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>


    Но если вы хотите там вызывать только обработку представления, например, запретить определённые клавиши (то есть, не нужны данные из слоя бизнес-логики), то есть смысл обработку сделать обычным способом - в code-behind (xaml.cs).
    Ответ написан
    Комментировать
  • Как сделать привязку данных по выбору?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно попробовать сделать третье свойство, которое в геттере будет получать нужное свойство.
    Ответ написан
    6 комментариев
  • Как загрузить UserControl при клике на элемент ListBox?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вам нужно просто связать датаконтекст групбоксов с выделенным элементом списка. Всё остальное сделает WPF.
    <ListBox ItemsSource="{Binding DevList}" SelectedItem="{Binding SelectedElement}" />
    <GroupBox DataContext="{Binding SelectedElement}">
    </GroupBox>
    <GroupBox DataContext="{Binding SelectedElement.Subelement1}">
    </GroupBox>


    Чтобы это заработало, то нужно сделать так, чтобы DevList был список объектов, а не строк. Создайте класс (вьюмодель) для ваших элементов списка. В нём можно хранить те свойства, которые вы хотите показывать в групбоксах. То есть, в списке элементов хранятся ваши объекты, а потом они же указываются датаконтекстом в другие контролы (групбоксы).

    В таком случае хорошо сделать во вьюмодели реализацию INotifyPropertyChanged.
    Ответ написан
  • C# WPF почему при Window.Left = 0 между границей экрана и окном появляется пустое пространство?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Это хром окна (WindowChrome), обычно выглядит как тень вокруг окна, является частью окна, поверх клиентской частью. Обычно размер хрома 7px.
    https://docs.microsoft.com/ru-ru/dotnet/api/system...

    Хром отключается при указании окна в развёрнутое состояние (Maximized). А также, можно указать WindowStyle.None окну, тогда неклиентская часть отключится.
    Ответ написан
    Комментировать
  • Как привязать данные разного типа к одному элементу управления и выводить в зависимости от определенного условия?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно сделать шаблоны DataTemplate, которые изменят шаблон нужного типа:
    <Window x:Class="TestApp.MainWindow"
            xmlns:testApp="clr-namespace:TestApp"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <DataTemplate DataType="{x:Type testApp:TestInt}">
                <TextBlock Text="{Binding Value}" Background="Red"/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type testApp:TestStr}">
                <TextBlock Text="{Binding Value}" Background="Yellow"/>
            </DataTemplate>
        </Window.Resources>
        <StackPanel>
                <ContentControl Content="{Binding Test1}"/>
                <ContentControl Content="{Binding Test2}"/>
                <ListBox ItemsSource="{Binding TestList}"/>
        </StackPanel>
    </Window>

    public class TestInt
    {
        public int Value { get; set; }
    }
    public class TestStr
    {
        public string Value { get; set; }
    }
    public partial class MainWindow : Window
    {
        public TestInt Test1 { get; set; }
        public TestStr Test2 { get; set; }
        public List<object> TestList { get; set; } 
    
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            Test1 = new TestInt { Value = 123 };
            Test2 = new TestStr { Value = "asd" };
            TestList = new List<object> { Test1, Test2 };
        }


    Чтобы даташаблоны автоматически подключались, тип нужно указывать через DataType="{x:Type testApp:TestInt}", а не через DataType="testApp:TestInt"
    Ответ написан
    Комментировать
  • Как обработать событие из DataTemplate нажатия радиокнопок?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    ListBox (как и другой наследник ItemsControl) имеет список элементов (удобнее всего его указывать через ItemsSource). Каждый элемент списка строится отдельным пунктом. У каждого пункта автоматически устанавливается DataContext - тот элемент исходного списка, который строится. В DataTemplate хорошо и правильно указывать нужный тип, который будет у элемента в датаконтексте, тогда появится подсказка у {Binding} внутри шаблона.

    public class Item : BaseViewModel
    {
        private string _name;
        public string Name 
        { 
            get => _name;
            set
            {
                _name = value;
                RaisePropertyChanged();
            }
        }
        
        private int _itemMode;
        public int ItemMode 
        { 
            get => _itemMode;
            set
            {
                _itemMode = value;
                RaisePropertyChanged();
            }
        }
    }
    
    public class ItemsList
    {
        public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>
        {
            new Item { Name = "Один", ItemMode = 1 },
            new Item { Name = "Два", ItemMode = 2 },
            new Item { Name = "Три", ItemMode = 3 },
        };
    }

    <Grid>
        <Grid.Resources>
            <converters:IntToBoolConverter x:Key="IntToBoolConverter"/>
            <DataTemplate DataType="{x:Type viewModels:Item}">
                <Border x:Name="Data">
                    <Grid x:Name="ContentBase" Margin="1,0">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <Label Name="DisplayText" Grid.Column="0" Content="{Binding Name}"/>
                        <RadioButton Name="_1" Grid.Column="1" Content="1" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=1}"/>
                        <RadioButton Name="_2" Grid.Column="2" Content="2" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=2}"/>
                        <RadioButton Name="_3" Grid.Column="3" Content="3" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=3}"/>
                    </Grid>
                </Border>
            </DataTemplate>
        </Grid.Resources>        
        <Grid.DataContext>
            <viewModels:ItemsList/>
        </Grid.DataContext>
        <ListBox ItemsSource="{Binding Items}"/>
    </Grid>

    Код класса BaseViewModel
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        
        protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    Код конвертера
    public class IntToBooleanConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value != null 
                && parameter != null 
                && value.ToString() == parameter.ToString();
        }
     
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || parameter == null) return Binding.DoNothing;
    
            int result;
            if ((bool)value && int.TryParse(parameter.ToString(), out result))
            {
                return result;
            }
    
            return Binding.DoNothing;
        }
    }

    Ответ написан
    8 комментариев
  • Как анимировать image в wpf при нажатии на другую кнопку?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    По кнопке изменить свойство во вьюмодели (возможно, булевое свойство). Триггер по изменению этого свойства вызовет анимацию.
    Ответ написан
    1 комментарий
  • Как включить отображение XAML Designer Window в Visual Studio 2017?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Судя по картинке, у вас студия разделила xaml и xaml.cs, а не поняла их как общую сущность. И теперь xaml показывает как простой текстовый файл. Возможно, студия не поняла, какой тип шаблона этого проекта (может, проект не созданный вновь, а подключенный существующий?).

    Создайте в этом же решении новый проект с типом WpfApplication и попробуйте дизайнер в нём. Сравните файлы csproj между вашим дефектным проектом и вновь созданным проектом.
    Ответ написан
    Комментировать
  • Есть ли большой туториал для изучения С# desktop + DB?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Лучшей книгой для вас будет Эндрю Троелсен "Язык программирования C# 6.0 и платформа .NET 4.6".
    Прекрасная книга для профессионалов, рассказывает обо всём в языке и почти обо всех возможностях и использованиях. В первую очередь, предназначена для программистов, которые уже умеют программировать, но не знают язык C#. В ей всё структурировано на разделы, каждый из них можно читать независимо и использовать как справочник.
    У этой книги есть только один недостаток - написана для профессионалов, и автор пишет, как делать, но не пишет, почему так делать - вы и так уже это понимаете, иначе книга стала бы совсем уж гигантской.

    Рекомендую взять время и попробовать написать несколько консольных программ на c#, начиная с моего любимого решателя квадратных уравнений :) и работы со списками (простой телефонный справочник). И только после этого начинайте работать с WPF.

    А вот очень приличный учебник по wpf:
    https://professorweb.ru/my/WPF/base_WPF/level1/inf...
    Довольно суховато написано, но с неплохими примерами.

    И опять же, рекомендую не начинать делать вашу программу, а сделать с помощью WPF те же программы, которые написали с консолью. И освойтё MVVM.
    Ответ написан
    1 комментарий
  • Wpf listview: как обновить только один элемент его?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    1) Список моделей хранить в ObservableCollection,
    2) В классе модели реализовать интерфейс INotifyPropertyChanged (можно сделать это в базовом классе)
    3) В свойствах модели в сеттере вбрасывать событие PropertyChanged с именем свойства:
    private string _name;
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            if (_name != value)
            {
                _name= value;
                RaisePropertyChanged(nameof(Name));
            }
        }
    }
    
    // можно сделать в базовом классе, в котором и реализовать интерфейс INotifyPropertyChanged
    protected virtual void RaisePropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    Ответ написан
    Комментировать
  • Как правильно открыть окно в другом приложении?

    lexxpavlov
    @lexxpavlov Автор вопроса
    Программист, преподаватель
    В описании функции SetForegroundWindow написаны условия, когда эта функция работает. В некоторых случаях почему-то в другое окно не передаётся клавиатурный фокус. Точнее, второй процесс, который открывает окно, не имеет фокуса. Почему-то в некоторых случаях это не мешает ему открывать активное окно (видимо, одно из других условий срабатывает), но в некоторых - не может сделать окно активным.

    Решение - симулировать нажатие клавиши в процессе, который будет открывать окно:
    private const byte VK_LMENU = 0xA4;
    private const byte KEYEVENTF_EXTENDEDKEY = 0x01;
    private const byte KEYEVENTF_KEYUP = 0x02;
    private const byte KEYSCAN_LALT = 0xB8;
    
    [DllImport("user32.dll")]
    internal static extern bool SetForegroundWindow(IntPtr hwnd);
    [DllImport("user32.dll")]
    private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
    
    public static void BringWindowToFront(IntPtr hwnd)
    {
        // симуляция нажатия клавиши ALT https://stackoverflow.com/a/13881647/1343405
        keybd_event(VK_LMENU, KEYSCAN_LALT, 0, 0);
        keybd_event(VK_LMENU, KEYSCAN_LALT, KEYEVENTF_KEYUP, 0);
    
        SetForegroundWindow(hwnd);
    }


    Полезные ссылки: вопрос, статья.
    Ответ написан
    Комментировать
  • Как создать общее свойство для элементов?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Нужно указать тип элементов, которым требуется указать стили. В DataGrid ячейки находятся в элементе DataGridCell, а заголовки - в DataGridColumnHeader.
    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Qwe"/>
            <DataGridTextColumn Header="Rty"/>
            <DataGridTextColumn Header="Uio"/>
        </DataGrid.Columns>
        <DataGrid.Resources>
            <Style TargetType="DataGridColumnHeader">
                <Setter Property="Width" Value="100"/>
            </Style>
        </DataGrid.Resources>
    </DataGrid>

    Можно положить стиль не в ресурсы, а в специальный контейнер. Разница в том, что стиль в ресурсах применяется во все вложенные подходящие элементы, даже вложенные внутри ячеек (например, в другой DataGrid внутри ячейки). Стиль в ColumnHeaderStyle будет использован только в заголовках этого DataGrid. Пример:
    <DataGrid.ColumnHeaderStyle>
        <Style TargetType="DataGridColumnHeader">
            <Setter Property="Width" Value="100"/>
        </Style>
    </DataGrid.ColumnHeaderStyle>

    Если указывать стиль элементов, то этот стиль не добавляется к текущему, а заменяет предыдущий стиль. Чтобы стиль добавлялся к уже существующем стилю, то стиль нужно наследовать от старого:
    <DataGrid.ColumnHeaderStyle>
        <Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
            <Setter Property="Width" Value="100"/>
        </Style>
    </DataGrid.ColumnHeaderStyle>
    Ответ написан
    4 комментария
  • Как изменить задний фон дочерних MenuItem?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Нужно переопределить шаблон элемента меню - просто добавить этот код в Window.Resources и всё, даже добавлять в <Menu> ничего не надо. "MenuItem.TopLevelHeaderTemplateKey" - стандартное название шаблона
    <ControlTemplate x:Key="{x:Static MenuItem.TopLevelHeaderTemplateKey}" TargetType="{x:Type MenuItem}">
        <Border Name="Border" >
            <Grid>
                <ContentPresenter Margin="6,3,6,3" ContentSource="Header" RecognizesAccessKey="True" />
                <Popup Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsSubmenuOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Fade">
                    <Border Name="SubmenuBorder" SnapsToDevicePixels="True" Background="Transparent">
                        <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle" />
                    </Border>
                </Popup>
            </Grid>
        </Border>
    </ControlTemplate>


    Решение вот отсюда. Там они ещё триггеры используют, но здесь не обязательно.
    Ответ написан
    1 комментарий
  • Как разделить код ViewModel-ей на каждое окно?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Я правильно понял, что есть несколько моделей (полученных из сервиса) и есть вьюмодель для каждой модели. И в какой-то момент происходит выбор показа нужной модели, это уже есть. Я правильно понял?

    Я бы попробовал в MainViewModel сделать список ViewModel-ей. Если добавляется новая модель, то добавлять в список. В MainViewModel добавить CurrentViewModel, имеющий ссылку на текущую вьюмодель, которая в данный момент нужно выводить. И в MainViewModel сделать метод, которыйпоказывает нужную вьюмодель по выбранной модели (что-то типа private ViewModel SelectModel(Model model) {}).
    Ответ написан
    Комментировать
  • Можно ли указать в одном биндинге несколько конвертеров?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вам не нужно делать второй конвертер, а настроить тот, чтобы возвращал нужное значение - возвращать Visibility.Collapsed при истине и Visibility.Visible при лжи.
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if ((bool)value)
        {
            return parameter != null ? Visibility.Collapsed : Visibility.Hidden;
        }
        return Visibility.Visible;
    }


    А если нужно именно проверять несколько различных проверок у одного элемента, то используйте MultiBinding. Я написал об этом тут.
    Ответ написан
    9 комментариев
  • Двойной клик по итему ListBox-а?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Нужно просто поставить привязку с командой у вашей вьюмодели. Вы ведь используете MVVM?

    Гораздо удобнее использовать не создавать вручную ICommand, а сделать класс, реализующий этот интерфейс. Обычно его называют DelegateCommand (классы указаны ниже).

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

    #region Свойства
    
    public ObservableCollection<MyClass> Collection { get; set; } = new ObservableCollection<MyClass>
    {
        new MyClass { Name = "Иванов Иван" },
        new MyClass { Name = "Петров Пётр" },
        new MyClass { Name = "Сидоров Сидор" },
    };
    public MyClass SelectedCollection { get; set; };
    
    #endregion
    
    #region Команды
    
    private ActionCommand _myCommand;
    public ActionCommand MyCommand => _myCommand ?? (_myCommand = new ActionCommand(MyMethod));
    
    private DelegateCommand<MyClass> _myDelegateCommand;
    public DelegateCommand<MyClass> MyDelegateCommand => _myDelegateCommand ?? (_myDelegateCommand = new DelegateCommand<MyClass>(MyMethod2, item => item != null);
    
    private void MyMethod()
    {
        // обработка команды
    }
    
    private void MyMethod2(MyClass item)
    {
        // обработка команды
    }
    
    #endregion


    <ListBox ItemsSource="{Binding Collection}" SelectedItem="{Binding SelectedItem}"/>
    <Button Command="{Binding MyCommand}" Content="Команда 1"/>
    <Button Command="{Binding MyDelegateCommand}" CommandParameter="{Binding SelectedItem}" Content="Команда 2"/>


    Если нужна команда не на событие Сlick, а на любое другое событие (как вы говорите, на DoubleClick), то нужно сделать добавить референс на System.Windows.Interactivity и указать триггер на событие:
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

    <ListBox ItemsSource="{Binding Collection}" SelectedItem="{Binding SelectedItemc}" DisplayMemberPath="Name">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseDoubleClick">
                <i:InvokeCommandAction Command="{Binding MyDelegateCommand}" CommandParameter="{Binding SelectedItem}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </ListBox>


    Класс DelegateCommand - команда с параметром, и класс ActionCommand - команда без параметра. Нешаблонный класс DelegateCommand используется реже (я вообще не припомню, чтобы его использовал).
    public class DelegateCommand<T> : ICommand
    {
        #region Private fields
    
        private readonly Action<T> _execute;
        private readonly Func<T, bool> _canExecute;
    
        #endregion
    
        #region Constructors
    
        public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        #endregion
    
        #region DelegateCommand
    
        public void Execute(T parameter)
        {
            var handler = _execute;
            if (handler != null)
            {
                handler(parameter);
            }
        }
    
        public bool CanExecute(T parameter)
        {
            var handler = _canExecute;
            return handler == null || handler(parameter);
        }
    
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
        #endregion
    
        #region ICommand
    
        void ICommand.Execute(object parameter)
        {
            Execute((T)parameter);
        }
    
        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute((T)parameter);
        }
    
        #endregion
    }
    
    public class DelegateCommand : DelegateCommand<object>
    {
        public DelegateCommand(Action<object> execute, Func<object, bool> canExecute = null)
            : base(execute, canExecute)
        {
        }
    }
    
    public class ActionCommand : DelegateCommand<object>, ICommand
    {
        #region Private fields
    
        private readonly Action _action;
        private readonly Func<bool> _canExecute;
    
        #endregion
    
        #region Constructors
    
        public ActionCommand(Action action, Func<bool> canExecute = null)
            : base(null, null)
        {
            _action = action;
            _canExecute = canExecute;
        }
    
        #endregion
    
        #region ActionCommand
    
        public void Execute()
        {
            var handler = _action;
            if (handler != null)
            {
                handler();
            }
        }
    
        public bool CanExecute()
        {
            var handler = _canExecute;
            return handler == null || handler();
        }
    
        #endregion
    
        #region ICommand
    
        void ICommand.Execute(object parameter)
        {
            Execute();
        }
    
        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute();
        }
    
        #endregion
    }
    Ответ написан
    1 комментарий
  • Почему не работает Binding?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Наверху {Binding Path=Dat1}, внизу {Binding Path=Dt1}. Опечатка - не хватает символа a.

    При запуске программы в студии в окне Output часто выводятся диагностические сообщения, и подобные ошибки там выводятся.
    Ответ написан
    7 комментариев
  • При выполнении обрезаются края окна в WPF. В чем тут проблема?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Не могу у себя проверить, у меня всё видно хорошо (у меня Win7).

    Попробуйте убрать все настройки внешнего вида. Будет ли нормально видно?
    <StackPanel>
        <Label Visibility="Collapsed" Content="Неверные данные авторизации" HorizontalAlignment="Center" Margin="10,7,10,0" Width="Auto" Foreground="Red"/>
        <Label Content="Логин:"/>
        <TextBox x:Name="loginTxt" TextWrapping="NoWrap"/>
        <Label Content="Пароль:"/>
        <PasswordBox x:Name="passwordTxt"/>
        <Button Content="Войти"/>
    </StackPanel>

    Если не изменится, то уберите атрибуты в Window.
    Если заработало, то постепенно возвращайте атрибуты в элементы. Или установите общий стиль для всех элементов сразу, чтобы для всех элементов стали одинаковые стили:
    <Window.Resources>
        <Style x:Key="CommonStyle" TargetType="Control">
            <Setter Property="Margin" Value="5" />
            <Setter Property="Padding" Value="5" />
            <Setter Property="FontSize" Value="16"/>
            <Setter Property="Width" Value="150"/>
        </Style>
        <Style TargetType="Label" BasedOn="{StaticResource CommonStyle}"/>
        <Style TargetType="TextBox" BasedOn="{StaticResource CommonStyle}"/>
        <Style TargetType="PasswordBox" BasedOn="{StaticResource CommonStyle}"/>
        <Style TargetType="Button" BasedOn="{StaticResource CommonStyle}"/>
    </Window.Resources>
    Ответ написан
    3 комментария
  • Не могу вызвать private C#?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Во-первых, приводите текст ошибки, чтобы мы могли вам помочь.
    А во-вторых, в методе test1() не хватает точки с запятой в третьей строчке.
    Ответ написан
    Комментировать