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

    Casper-SC
    @Casper-SC
    .NET программист
    Как организовать постоянное обновление DataGrid в отдельном потоке, чтобы при этом DataGrid был доступен, т.к. планируется добавить возможность выбирать строку из DataGrid и смотреть подробную информацию.

    Нельзя обновлять элементы UI из не UI потока. Можно получать данные в другом потоке, но обновлять UI нужно из UI потока. Я бы просто пробегал по ObservableCollection<{T}>, где T это класс, реализующий интерфейс INotifyPropertyChanged и обновлял нужные объекты уже в UI потоке. Судя по всему, проект не придерживается паттерна MVVM, а лучше бы придерживался.

    Список процессов нужно получать как раз в Task.Run(...), а вот вызывать обновление процессов в UI через Dispatcher или SynchronizationContext.

    Посмотри мои ответы здесь:
    Как осуществить ввод и вывод данных WPF?
    Здесь про Task.Delay и вообще цикл в Task.Run Как сделать цикл на основе ответа на Web запрос в C#?
    Здесь как перенаправлять выполнение через SynchronizationContext в UI поток С# Taks и Invoke почему то блокируется форма?

    Ещё, ожидание в методах, которые запускаются тасками нужно делать через вызов await Task.Delay(...), но точно не через Thread.Sleep(...):
    using System;
    using System.Threading.Tasks;
    
    namespace Tasks
    {
        class Program
        {
            static async Task Main(string[] args)
            {
                // Так
                await Task.Run(async () =>
                {
                    while (true)
                    {
                        await Task.Delay(1000);
                        Console.Write("=");
                    }
                });
    
                // Или так (обрати внимание на Unwrap()) 
                await Task.Factory.StartNew(async () =>
                {
                    while (true)
                    {
                        await Task.Delay(1000);
                        Console.Write("=");
                    }
                }).Unwrap();
            }
        }
    }
    Ответ написан
  • WPF vs UWP в 2021?

    Casper-SC
    @Casper-SC
    .NET программист
    • Между UWP и WPF есть ещё отличие в жизненном цикле приложения. UWP приложение не совсем тоже самое, что и WPF.
    • В UWP преобладает страничный режим. Там всё построено на переходах между страницами.
    • Приложения UWP устанавливаются из Microsoft Store.
    • Приложения UWP требуют включения разных разрешений для приложения.

    5fe41c979dcc6193298625.png
    Ответ написан
  • Почему в WPF не работает перенаправление на другое окно?

    Casper-SC
    @Casper-SC
    .NET программист
    Посмотри рабочий пример реализации здесь:
    Как осуществить переход между страницами в Windows Presentation Foundation?

    У тебя просто открыто окно и в нём вызывается работа сервиса. Это не означает, что MainWindow закроется и откроется новое окно. Нужно использовать или Frame или NavigationWindow (справка поддерживает русский язык, смени en-us на ru-ru).
    Ответ написан
  • Как взаимодействовать с элементом внутри DataTemplate?

    Casper-SC
    @Casper-SC
    .NET программист
    Можно ещё сделать свой UserControl, в который поместить Ellipse и всю логику модификации его внешнего вида в XAML. Но нужно ли оно? Думаю, нет. Способов много это сделать.

    <DataGrid
        AutoGenerateColumns="False"
        ItemsSource="{Binding Income}">
        <DataGrid.Columns>
            <DataGridTemplateColumn
                CanUserReorder="False"
                CanUserResize="False"
                CanUserSort="False"
                IsReadOnly="True">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate DataType="viewModels:IncomeViewModel">
                        <Ellipse Fill="{Binding IsModified, Converter={StaticResource BooleanToBrushConverter}}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
    </DataGrid>


    using GalaSoft.MvvmLight;
    
    namespace WpfApp.ViewModels
    {
        public class IncomeViewModel : ViewModelBase
        {
            private bool _isModified;
    
            public bool IsModified
            {
                get { return _isModified; }
                set { Set(ref _isModified, value); }
            }
        }
    }


    using System;
    using System.Globalization;
    using System.Windows.Data;
    using System.Windows.Media;
    
    namespace WpfApp.Converters
    {
        [ValueConversion(typeof(bool), typeof(SolidColorBrush))]
        public class BooleanToBrushConverter : IValueConverter
        {
            public SolidColorBrush TrueBrush { get; set; } = new SolidColorBrush(Colors.Tomato);
    
            public SolidColorBrush FalseBrush { get; set; } = new SolidColorBrush(Colors.CornflowerBlue);
    
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return (bool)value ? TrueBrush : FalseBrush;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return Binding.DoNothing;
            }
        }
    }
    Ответ написан
  • Как осуществить ввод и вывод данных WPF?

    Casper-SC
    @Casper-SC
    .NET программист
    Обновлено:
    Ответ не совcем верный в том плане, что здесь нужно, как сейчас видится четыре DataGrid'a. Где у вас написано Зарплата, Сумма, Аренда, Сумма это всё данные одного DataGrid должны быть.
    5fc24d309b48a634078946.png
    *.csproj
    <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
    
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>netcoreapp3.1</TargetFramework>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="MvvmLightLibsStd10" Version="5.4.1.1" />
      </ItemGroup>
    
    </Project>


    <DataGrid
        Grid.Row="1"
        Grid.Column="1"
        AutoGenerateColumns="False"
        ItemsSource="{Binding Income}">
        <DataGrid.Columns>
            <DataGridTextColumn
                Width="67"
                Binding="{Binding Title}"
                Header="Title" />
            <DataGridTextColumn
                Width="67"
                Binding="{Binding Sum}"
                Header="Sum" />
        </DataGrid.Columns>
    </DataGrid>


    using System.Collections.ObjectModel;
    using GalaSoft.MvvmLight;
    
    namespace WpfApp1.ViewModels
    {
        public class MainViewModel : ViewModelBase
        {
            public ObservableCollection<IncomeViewModel> Income { get; }
    
            public MainViewModel()
            {
                Income = new ObservableCollection<IncomeViewModel>();
                // Просто для теста заполняем
                Income.Add(new IncomeViewModel { Title = "Зарплата", Sum = 100_000m });
            }
        }
    
        /// <summary>
        /// Модель представления одной строки в таблице Доход.
        /// </summary>
        public class IncomeViewModel : ViewModelBase
        {
            private string _title;
            private decimal _sum;
    
            public string Title
            {
                get { return _title; }
                set { Set(ref _title, value); }
            }
    
            public decimal Sum
            {
                get { return _sum; }
                set { Set(ref _sum, value); }
            }
        }
    }


    -------------

    У каждого DataGrid задайте свойства
    AutoGenerateColumns="False"
    ItemsSource="{Binding Elements}"


    Где Elements - это у каждого DataGrid своя коллекция данных во вью модели (Названия, понятное дело у них будут разные).

    Ещё можно задать в DataGrid'ах, где очень много данных (VirtualizingStackPanel):
    <DataGrid
        AutoGenerateColumns="False"
        ItemsSource="{Binding Elements}">
        <DataGrid.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel
                    IsVirtualizing="True"
                    VirtualizationMode="Recycling" />
            </ItemsPanelTemplate>
        </DataGrid.ItemsPanel>
    </DataGrid>
    Ответ написан
  • Как правильно реализовать информирование ViewModel'и по MVVM?

    Casper-SC
    @Casper-SC
    .NET программист
    Читай комментарии в коде, в том числе зачем там lock. Вдруг ты будешь использовать такой поиск с отменой не в UI потоке (не именно этот код, а саму логику).

    Models/SearchAlgorithm.cs
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace EventsInModel.Models
    {
        public class SearchAlgorithm
        {
            public string CurrentFolder { get; private set; }
    
            public event EventHandler ProgressChanged;
    
            public async Task Search(CancellationToken cancellationToken)
            {
                for (int i = 0; i < 5; i++)
                {
                    await Task.Delay(1200, cancellationToken);
    
                    cancellationToken.ThrowIfCancellationRequested();
    
                    // Можно прогресс передавать и в качестве аргумента события,
                    // но в данном случае, вряд ли это оправдано. Обработав событие можно получить
                    // доступ к отправителю события и прочитать его свойства.
                    CurrentFolder = i.ToString();
                    ProgressChanged?.Invoke(this, EventArgs.Empty);
                }
            }
        }
    }


    ViewModels/MainViewModel.cs
    using System;
    using System.Threading;
    using System.Windows.Input;
    using EventsInModel.Models;
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Command;
    
    namespace EventsInModel.ViewModels
    {
        // ViewModelBase из библиотеки MvvmLight
        public class MainViewModel : ViewModelBase
        {
            //private readonly object _sync = new object();
            private readonly SearchAlgorithm _search;
    
            private string _currentFolder;
            // Логику с отменой можно вынести в отдельный класс, чтобы не писать простыню
            // с отменой в каждом таком месте с операцией, которая может быть отменена, а 
            // в UI приложениях такое сплошь и рядом.
            private volatile CancellationTokenSource _lastCancellationTokenSource;
    
            public string CurrentFolder
            {
                get { return _currentFolder; }
                private set { Set(ref _currentFolder, value); }
            }
    
            public ICommand SearchCommand { get; }
    
            public MainViewModel(SearchAlgorithm search)
            {
                _search = search;
                _search.ProgressChanged += OnSearchProgressChanged;
                SearchCommand = new RelayCommand(Search);
            }
    
            public override void Cleanup()
            {
                //lock (_sync)
                {
                    _lastCancellationTokenSource?.Cancel();
                }
    
                _search.ProgressChanged -= OnSearchProgressChanged;
                base.Cleanup();
            }
    
            /// <summary>
            /// Прерывает прошлый поиск и запускает новый.
            /// </summary>
            private async void Search()
            {
                CancellationTokenSource currentTokenSource;
                // В случае, если такой метод вызывать не из UI потока, то lock здесь нужен
                // Если использовать только из UI потока как здесь, то lock можно удалить.
                // Ещё бы я вынес логику в отдельный класс и использовал в других проектах в том числе.
                //lock (_sync)
                {
                    _lastCancellationTokenSource?.Cancel();
                    currentTokenSource = new CancellationTokenSource();
                    _lastCancellationTokenSource = currentTokenSource;
                }
    
                try
                {
                    await _search.Search(currentTokenSource.Token);
                }
                catch (OperationCanceledException)
                {
                    // Ignored.
                }
                finally
                {
                    //lock (_sync)
                    {
                        currentTokenSource.Dispose();
                        if (ReferenceEquals(_lastCancellationTokenSource, currentTokenSource))
                        {
                            _lastCancellationTokenSource = null;
                        }
                    }
                }
            }
    
            private void OnSearchProgressChanged(object sender, EventArgs e)
            {
                var search = (SearchAlgorithm)sender;
                CurrentFolder = search.CurrentFolder;
            }
        }
    }


    Затестил я по быстрому так. Это тебе не особо нужно:

    MainWindow.xaml.cs
    using System.Windows;
    using EventsInModel.Models;
    using EventsInModel.ViewModels;
    
    namespace EventsInModel
    {
        public partial class MainWindow : Window
        {
            private readonly MainViewModel _viewModel;
    
            public MainWindow()
            {
                InitializeComponent();
                _viewModel = new MainViewModel(new SearchAlgorithm());
                DataContext = _viewModel;
    
                Loaded += OnLoaded;
                Closing += OnClosing;
            }
    
            private void OnLoaded(object sender, RoutedEventArgs e)
            {
                _viewModel.SearchCommand.Execute(null);
                _viewModel.SearchCommand.Execute(null);
            }
    
            private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
            {
                _viewModel.Cleanup();
            }
        }
    }


    Проект с зависимостями
    <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
    
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>netcoreapp3.1</TargetFramework>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="MvvmLightLibsStd10" Version="5.4.1.1" />
      </ItemGroup>
    
    </Project>
    Ответ написан
  • Задать текст вместо картинки в MenuItem?

    Casper-SC
    @Casper-SC
    .NET программист
    Тебе нужно на меню (MenuItem выберется, если меню не пустое) навести курсор мыши и нажать правую кнопку мыши, выбрать EditTemplate -> Edit a copy. Создастся стиль с шаблонами.

    Там ты увидишь такой код
    <ControlTemplate
        x:Key="{ComponentResourceKey ResourceId=SubmenuItemTemplateKey,
                        TypeInTargetAssembly={x:Type MenuItem}}">
    <!-- Много кода -->
    </ControlTemplate>

    Ищи там

    Это та штука, которая отображает иконку. Как видно, она отображает то, что задано в свойстве Icon (см. ContentSource="Icon").
    <ContentPresenter
        x:Name="Icon"
        Width="16"
        Height="16"
        Margin="3"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        ContentSource="Icon"
        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />


    Задай свойству Width значение побольше. Margin слева и/или справа можешь уменьшить, если по дизайну это норм.
    Как видно, этот ContentPresenter отображает то, что содержится в свойстве Icon у MenuItem. Просто в Icon задай нужный текст.

    Вертикальная черта в том шаблоне это
    <Rectangle
        Width="1"
        Margin="29,2,0,2"
        HorizontalAlignment="Left"
        Fill="{StaticResource Menu.Static.Separator}" />


    <Menu
        Grid.Row="0"
        Height="25"
        VerticalAlignment="Top"
        DockPanel.Dock="Top">
        <MenuItem Style="{StaticResource MenuItemStyle}" Header="Тесты">
            <MenuItem
                Icon="101"
                Command="{Binding WorkspaceViewModel.UpdateTestsCommand}"
                Header="Обновить список тестов"
                IsEnabled="{Binding Mode, Converter={StaticResource ApplicationModeEnumToBooleanConverter}, ConverterParameter='Tests'}" />
             <!-- -->
        </MenuItem>
        <MenuItem Header="Режим">
             <!-- -->
        </MenuItem>
    </Menu>


    5f999711c2b74120986977.png
    Ответ написан
  • Как в DataGrid задать оформление определенному полю?

    Casper-SC
    @Casper-SC
    .NET программист
    Сюда нельзя выложить много кода, остальное в комментариях. Этот пример не сильно опирается на Mvvm, так что в коде окна расположена коллекция, а не вью модели. Используется библиотека MvvmLight.

    Views/MainWindow.xaml
    <Window
        x:Class="WpfDataGrid.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        xmlns:viewModels="clr-namespace:WpfDataGrid.ViewModels"
        Title="MainWindow"
        Width="800"
        Height="450"
        DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}"
        WindowStartupLocation="CenterScreen"
        mc:Ignorable="d">
        <Window.Resources>
    
            <SolidColorBrush
                x:Key="BackgroundOfSelectedDataGridRow"
                Color="#B6B6B6" />
            <SolidColorBrush
                x:Key="BorderBrushOfSelectedDataGridRow"
                Color="#FF2485C9" />
            <SolidColorBrush
                x:Key="ForegroundOfSeletedDataGridRow"
                Color="Black" />
    
            <SolidColorBrush
                x:Key="GridLinesBrush"
                Color="#FFB0B0B0" />
    
            <FontFamily x:Key="DefaultFontFamylyKey">
                Microsoft Sans Serif
            </FontFamily>
    
            <Style
                x:Key="DefaultDataGridCellStyle"
                TargetType="{x:Type DataGridCell}">
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Background" Value="#B2FFD0A2" />
                        <Setter Property="BorderBrush" Value="#99FFE5CC" />
                        <Setter Property="Foreground" Value="Black" />
                    </Trigger>
                </Style.Triggers>
            </Style>
            <Style
                x:Key="DataGridStyle"
                TargetType="{x:Type DataGrid}">
                <Setter Property="RowBackground" Value="#FFE6E6E6" />
                <Setter Property="AlternatingRowBackground" Value="#FFF1F1F1" />
                <Setter Property="AlternationCount" Value="2" />
                <Setter Property="HorizontalGridLinesBrush" Value="{StaticResource GridLinesBrush}" />
                <Setter Property="VerticalGridLinesBrush" Value="{StaticResource GridLinesBrush}" />
                <Setter Property="FontFamily" Value="{StaticResource DefaultFontFamylyKey}" />
                <Setter Property="FontSize" Value="15" />
                <Setter Property="SelectionMode" Value="Single" />
                <Setter Property="RowStyle">
                    <Setter.Value>
                        <Style TargetType="{x:Type DataGridRow}">
                            <Style.Triggers>
    
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Background" Value="{StaticResource BackgroundOfSelectedDataGridRow}" />
                                    <Setter Property="BorderBrush" Value="{StaticResource BorderBrushOfSelectedDataGridRow}" />
                                    <Setter Property="Foreground" Value="{StaticResource ForegroundOfSeletedDataGridRow}" />
                                    <Setter Property="Tag" Value="{Binding}" />
                                </Trigger>
    
                                <Trigger Property="IsSelected" Value="True">
                                    <Setter Property="Background" Value="#B2FFD0A2" />
                                    <Setter Property="BorderBrush" Value="#99FFE5CC" />
                                    <Setter Property="Foreground" Value="Black" />
                                </Trigger>
    
                            </Style.Triggers>
                        </Style>
    
                    </Setter.Value>
                </Setter>
    
                <Setter Property="CellStyle" Value="{StaticResource DefaultDataGridCellStyle}" />
    
            </Style>
        </Window.Resources>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
    
            <DataGrid
                Margin="10,10,10,0"
                AutoGenerateColumns="False"
                ItemsSource="{Binding Collection}"
                Style="{StaticResource DataGridStyle}">
                <DataGrid.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel
                            IsVirtualizing="True"
                            VirtualizationMode="Recycling" />
                    </ItemsPanelTemplate>
                </DataGrid.ItemsPanel>
    
                <DataGrid.Columns>
                    <DataGridTemplateColumn
                        x:Name="PropertiesUpdateIndicatorColumn"
                        CanUserReorder="False"
                        CanUserResize="False"
                        CanUserSort="False"
                        IsReadOnly="True">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate DataType="viewModels:ItemViewModel">
                                <Rectangle Fill="{Binding IsChecked, Converter={StaticResource BooleanToBrushConverter}}" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                        <DataGridTemplateColumn.CellEditingTemplate>
                            <DataTemplate DataType="viewModels:ItemViewModel">
                                <TextBlock Text="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellEditingTemplate>
                    </DataGridTemplateColumn>
    
                    <DataGridTemplateColumn
                        Width="50"
                        ClipboardContentBinding="{x:Null}"
                        Header="Выбран">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate DataType="viewModels:ItemViewModel">
                                <CheckBox
                                    HorizontalContentAlignment="Center"
                                    VerticalContentAlignment="Center"
                                    IsChecked="{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                        <DataGridTemplateColumn.HeaderTemplate>
                            <ItemContainerTemplate>
                                <TextBlock
                                    Text=""
                                    ToolTip="{Binding Path=Text, RelativeSource={RelativeSource Self}}"
                                    ToolTipService.HasDropShadow="False"
                                    ToolTipService.Placement="Relative">
                                    <TextBlock.Resources>
                                        <Style TargetType="ToolTip">
                                            <Setter Property="VerticalOffset" Value="-1" />
                                            <Setter Property="HorizontalOffset" Value="-1" />
                                            <Setter Property="OverridesDefaultStyle" Value="True" />
                                            <Setter Property="HasDropShadow" Value="False" />
                                        </Style>
                                    </TextBlock.Resources>
                                </TextBlock>
                            </ItemContainerTemplate>
                        </DataGridTemplateColumn.HeaderTemplate>
                    </DataGridTemplateColumn>
    
                    <DataGridTemplateColumn
                        x:Name="TitleColumn"
                        ClipboardContentBinding="{x:Null}"
                        Header="Название"
                        SortMemberPath="Title">
                        <DataGridTemplateColumn.HeaderTemplate>
                            <ItemContainerTemplate>
                                <TextBlock
                                    Text="Название"
                                    ToolTip="{Binding Path=Text, RelativeSource={RelativeSource Self}}"
                                    ToolTipService.HasDropShadow="False"
                                    ToolTipService.Placement="Relative">
                                    <TextBlock.Resources>
                                        <Style TargetType="ToolTip">
                                            <Setter Property="VerticalOffset" Value="-1" />
                                            <Setter Property="HorizontalOffset" Value="-1" />
                                            <Setter Property="OverridesDefaultStyle" Value="True" />
                                            <Setter Property="HasDropShadow" Value="False" />
                                        </Style>
                                    </TextBlock.Resources>
                                </TextBlock>
                            </ItemContainerTemplate>
                        </DataGridTemplateColumn.HeaderTemplate>
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding Title, Mode=OneWay}" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                        <DataGridTemplateColumn.CellEditingTemplate>
                            <DataTemplate>
                                <TextBox
                                    MaxLength="120"
                                    Text="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellEditingTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
    
            <Button
                Grid.Row="1"
                Width="75"
                Margin="10,6,0,10"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                Click="OnDeleteButtonClick"
                Content="Delete" />
        </Grid>
    </Window>
    Ответ написан
  • Как вставить векторное изображение в WPF окно?

    Casper-SC
    @Casper-SC
    .NET программист
    Изображение можно увеличить (кликни или открой в новой вкладке). XAML код отформатирован расширением XamlStyler.

    5f9489198d002927078688.png

    <Window
        x:Class="WpfVectorImage.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:icons="clr-namespace:WpfVectorImage.Icons"
        xmlns:local="clr-namespace:WpfVectorImage"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="800"
        Height="450"
        mc:Ignorable="d">
        <Grid>
            <icons:DumbbellIcon />
        </Grid>
    </Window>


    Нельзя сюда выложить иконку, которая на скриншоте, слишком много символов.
    DumbbellIcon.xaml (иконка)
    <UserControl
        x:Class="WpfVectorImage.Icons.DumbbellIcon"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:WpfVectorImage.Icons"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        d:DesignHeight="450"
        d:DesignWidth="800"
        mc:Ignorable="d">
    
        <Viewbox Stretch="Uniform">
            <Canvas
                Name="Layer_1"
                Width="512"
                Height="512">
                <Canvas.RenderTransform>
                    <TranslateTransform X="0" Y="0" />
                </Canvas.RenderTransform>
                <Canvas.Resources />
                <Canvas Name="g79">
                    <Canvas Name="g73">
    
                    </Canvas>
                </Canvas>
            </Canvas>
        </Viewbox>
    </UserControl>
    Ответ написан
  • Что не так в коде?

    Casper-SC
    @Casper-SC
    .NET программист
    1. Стиль именования какой-то странный и смешанный. Можно глянуть здесь, как нужно оформлять код (не навязываю, но это именно тот стиль, в котором написан .NET Core и прочий дотнет):
    Как можно проверить,что цифры числа расположены в возрастающем порядке или в убывающем?
    Как осуществить переход между страницами в Windows Presentation Foundation? (только закомментированный оставлять не нужно, здесь для примера)
    Как сделать считывание с input и вывод в output на C#?
    Не используй русские символы, транслит, и префиксы. Не оставляй гигантские пробелы между методами.

    2. async void processing. Здесь нужно возвращать Task и он должен ожидаться в месте вызова, даже если на этом заканчивается работа метода. Если этот код вызывается по клику, то что будет если кликнуть 2 или 10 раз?

    3. Метод processing не меняет значения текстовых полей, в коде идёт присвоение значений параметрам, но эти значения не попадут в текстовые поля в окне.

    4. Магические переменные i и v. Нужно давать понятные названия.

    int i = CarNumber.Length; // Я уже переименовал и свойства
    int v = CarMark.Length;


    5. Здесь вообще Task не нужен на такой задаче, если это примерно то кол-во данных, которое будет обрабатываться.

    6. Если бы даже код был написан так, что значения бы присваивались текстовым полям в окне, то приложение бы крашилось из-за обращения к UI элементам из не UI потока.

    7. Впечатление, что автор кода думает, что может быть ошибка в коде и переменная станет меньше нуля. Зачем здесь <=, если должно быть ==?
    if (i <= 0)
    {
        dataProperty = $"Данной машины не найдено на парковке";
        dataPlace = $"Повторите попытку";
    }


    8. Здесь не нужен знак $.
    $"Данной машины не найдено на парковке";

    9. В принципе класс-парсер построен неправильно.
    Ответ написан
  • Как осуществить переход между страницами в Windows Presentation Foundation?

    Casper-SC
    @Casper-SC
    .NET программист
    Microsoft Docs: How to: Navigate to a Page
    Microsoft Docs: NavigationWindow Class
    Хабр: WPF Navigation: Используем связку Page + Frame

    namespace PageNavigation.Services
    {
        public interface INavigationService
        {
            void NavigateToPage1();
    
            void NavigateToPage2();
        }
    }


    using System.Windows.Controls;
    using PageNavigation.Pages;
    
    namespace PageNavigation.Services
    {
        public class NavigationService : INavigationService
        {
            private readonly Frame _frame;
    
            public NavigationService(Frame frame)
            {
                _frame = frame;
            }
    
            public void NavigateToPage1()
            {
                _frame.Navigate(new Page1());
            }
    
            public void NavigateToPage2()
            {
                _frame.Navigate(new Page2());
            }
        }
    }


    Такой Ioc:
    using System.Windows.Controls;
    
    namespace PageNavigation.Services
    {
        public static class Ioc
        {
            public static INavigationService NavigationService { get; private set; }
    
            public static void Init(Frame frame)
            {
                NavigationService = new NavigationService(frame);
            }
        }
    }


    Или такой Ioc:
    https://autofac.org/
    using System.Windows.Controls;
    using Autofac;
    
    namespace PageNavigation.Services
    {
        public static class Ioc
        {
            private static IContainer _container;
    
            public static INavigationService NavigationService
            {
                get { return _container.Resolve<INavigationService>(); }
            }
    
            //public static MainViewModel MainViewModel
            //{
            //    get { return _container.Resolve<MainViewModel>(); }
            //}
    
            public static void Init(Frame frame)
            {
                var builder = new ContainerBuilder();
                
                builder.RegisterType<NavigationService>()
                    .As<INavigationService>()
                    .SingleInstance()
                    .WithParameter(new TypedParameter(typeof(Frame), frame));
    
                //builder.RegisterType<MainViewModel>()
                //    .SingleInstance();
    
                _container = builder.Build();
            }
        }
    }


    Главное окно XAML
    <Window
        x:Class="PageNavigation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="525"
        Height="350"
        Loaded="OnLoaded"
        WindowStartupLocation="CenterScreen"
        mc:Ignorable="d">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="266*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
    
            <Frame x:Name="_frame" />
    
            <StackPanel
                Grid.Row="1"
                Margin="8,0,0,6"
                Orientation="Horizontal">
    
                <Button
                    MinWidth="75"
                    MinHeight="29"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Top"
                    Click="OnNavigateToPage1ButtonClick"
                    Content="Page1" />
    
                <Button
                    MinWidth="75"
                    MinHeight="29"
                    Margin="8,0,0,0"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Top"
                    Click="OnNavigateToPage2ButtonClick"
                    Content="Page2" />
            </StackPanel>
        </Grid>
    </Window>


    Главное окно C#
    using System.Windows;
    using PageNavigation.Services;
    
    namespace PageNavigation
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void OnLoaded(object sender, RoutedEventArgs e)
            {
                Ioc.Init(_frame);
            }
    
            private void OnNavigateToPage1ButtonClick(object sender, RoutedEventArgs e)
            {
                Ioc.NavigationService.NavigateToPage1();
            }
    
            private void OnNavigateToPage2ButtonClick(object sender, RoutedEventArgs e)
            {
                Ioc.NavigationService.NavigateToPage2();
            }
        }
    }


    Страница 1 XAML
    <Page
        x:Class="PageNavigation.Pages.Page1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="Page1"
        d:DesignHeight="300"
        d:DesignWidth="300"
        mc:Ignorable="d">
    
        <Grid>
    
            <TextBlock Margin="5">
                Это Page 1
            </TextBlock>
    
            <Button
                Padding="12,3,12,3"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Click="OnNavigateToAnotherPageButtonClick"
                Content="Перейти на Page 2" />
        </Grid>
    </Page>


    Страница 1 C#
    using System.Windows.Controls;
    using PageNavigation.Services;
    
    namespace PageNavigation.Pages
    {
        public partial class Page1 : Page
        {
            public Page1()
            {
                InitializeComponent();
            }
    
            private void OnNavigateToAnotherPageButtonClick(object sender, System.Windows.RoutedEventArgs e)
            {
                Ioc.NavigationService.NavigateToPage2();
            }
        }
    }


    Страница 2 XAML
    <Page
        x:Class="PageNavigation.Pages.Page2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="Page2"
        d:DesignHeight="300"
        d:DesignWidth="300"
        mc:Ignorable="d">
    
        <Grid>
    
            <TextBlock Margin="5">
                Это Page 2
            </TextBlock>
    
            <Button
                Padding="12,3,12,3"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Click="OnNavigateToAnotherPageButtonClick"
                Content="Перейти на Page 1" />
        </Grid>
    </Page>


    Страница 2 C#
    using System.Windows;
    using System.Windows.Controls;
    using PageNavigation.Services;
    
    namespace PageNavigation.Pages
    {
        public partial class Page2 : Page
        {
            public Page2()
            {
                InitializeComponent();
            }
    
            private void OnNavigateToAnotherPageButtonClick(object sender, RoutedEventArgs e)
            {
                Ioc.NavigationService.NavigateToPage1();
            }
        }
    }
    Ответ написан
  • Как сделать, чтобы ProgressBar работал во время нагрузки на приложение?

    Casper-SC
    @Casper-SC
    .NET программист
    Сделал на основе вашего кода. Вообще нужно это всё писать немного иначе. Передавать ссылку в классы с алгоритмами на контролы - это очень плохая идея и сразу забудьте про такой подход.

    В этом примере если два раза нажать на пункт меню, то запустится два цикла в поток(е/ах) из пула потоков. Это уже сами обработаете.

    В данном примере Analyzer - это отдельная сущность, которая умеет уведомлять о каких-то своих внутренних событиях внешний мир посредством сигналов. Событие и есть тот самый сигнал, а обработать его сможет любое кол-во подписчиков. Событие можно воспринимать, как пины на плате Raspberry Pi, если к нему что-то подсоединить, то можно как-то реагировать на сигнал от пина, тут тоже самое.

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ProgressBarExample
    {
        internal class Analyzer
        {
            private readonly SynchronizationContext _synchronizationContext;
    
            public Analyzer()
            {
                // Если экземпляр класса будет создан в UI потоке,
                // то здесь будет контекст синхронизации UI потока, иначе пула потоков
                _synchronizationContext = SynchronizationContext.Current ?? new SynchronizationContext();
            }
    
            public event EventHandler<AnalyzerEventArgs> ProgressChanged;
    
            public async Task<Data> LoadDataInDB()
            {
                var result = await Task.Run(async () =>
                {
                    for (int i = 0; i < 100; i++)
                    {
                        await Task.Delay(250);
                        OnProgressChanged(new AnalyzerEventArgs("line " + (i + 1), 100));
                    }
                    return new Data() { Text = "Данные " };
                });
                return result;
            }
    
            private void OnProgressChanged(AnalyzerEventArgs args)
            {
                // Перенаправляем выполнение в UI поток не ожидая пока отработает метод обработчик события.
                _synchronizationContext.Post(state =>
                {
                    ProgressChanged?.Invoke(this, (AnalyzerEventArgs)state);
                }, args); // args передаётся в переменную state (грубо говоря)
            }
        }
    }


    namespace ProgressBarExample
    {
        public class AnalyzerEventArgs
        {
            public int MaxLines { get; }
    
            public string CurrentLine { get; }
    
            public AnalyzerEventArgs(string currentLine, int maxLines)
            {
                CurrentLine = currentLine;
                MaxLines = maxLines;
            }
        }
    }


    namespace ProgressBarExample
    {
        public class Data
        {
            public string Text { get; set; }
        }
    }


    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Input;
    
    namespace ProgressBarExample
    {
        public partial class MainWindow : Window, INotifyPropertyChanged
        {
            private readonly Analyzer _analyzer;
            private readonly Style _normalBarStyle;
            private readonly Style _loadBarStyle;
            private readonly Style _errorBarStyle;
            private string _maxLines;
            private string _currentLine;
    
            public string MaxLines
            {
                get { return _maxLines; }
                set
                {
                    _maxLines = value;
                    OnPropertyChanged();
                }
            }
    
            public string CurrentLine
            {
                get { return _currentLine; }
                set
                {
                    _currentLine = value;
                    OnPropertyChanged();
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public MainWindow()
            {
                InitializeComponent();
    
                _normalBarStyle = FindResource("NormalStatusBar") as Style;
                _loadBarStyle = FindResource("LoadDataStatusBar") as Style;
                _errorBarStyle = FindResource("ErrorStatusBar") as Style;
    
                _statusBar.Style = _normalBarStyle;
                
                _analyzer = new Analyzer();
                _analyzer.ProgressChanged += OnAnalyzerProgressChanged;
            }
    
            private void OnAnalyzerProgressChanged(object sender, AnalyzerEventArgs args)
            {
                // Передавать каждый раз одно и тоже бессмысленно, сделаете сами как нужно
                MaxLines = args.MaxLines.ToString();
                CurrentLine = args.CurrentLine;
            }
    
            private async void LoadDataToDB_Click(object sender, RoutedEventArgs e)
            {
                _statusBar.Style = _loadBarStyle;
                Mouse.OverrideCursor = Cursors.Wait;
    
                try
                {
                    var data = await _analyzer.LoadDataInDB(); 
                    MessageBox.Show(data.Text);
                }
                finally
                {
                    Mouse.OverrideCursor = Cursors.Arrow;
                    _statusBar.Style = _normalBarStyle;
                }
            }
    
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }


    <Window
        x:Class="ProgressBarExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:ProgressBarExample"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="800"
        Height="450"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        mc:Ignorable="d">
        <Window.Resources>
            <Style x:Key="NormalStatusBar" TargetType="DockPanel">
                <Setter Property="Background" Value="#FF007ACC" />
            </Style>
            <Style x:Key="LoadDataStatusBar" TargetType="DockPanel">
                <Setter Property="Background" Value="#9333FF" />
            </Style>
            <Style x:Key="ErrorStatusBar" TargetType="DockPanel">
                <Setter Property="Background" Value="#eb4034" />
            </Style>
        </Window.Resources>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="20" />
            </Grid.RowDefinitions>
            <Menu VerticalAlignment="Top">
                <MenuItem Header="File">
                    <MenuItem
                        x:Name="LoadDataToDB"
                        Click="LoadDataToDB_Click"
                        Header="LoadDataToDB" />
                </MenuItem>
            </Menu>
            <DockPanel
                x:Name="_statusBar"
                Grid.Row="1"
                Style="{StaticResource LoadDataStatusBar}">
                <!--  Style меняться во время выполнения LoadDataInDB()  -->
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <TextBlock
                        Grid.Column="0"
                        Margin="10,0,0,0"
                        Foreground="White"
                        Text="Статус данных" />
                    <DockPanel Grid.Column="1" HorizontalAlignment="Right">
                        <TextBlock
                            Margin="0,0,5,0"
                            Foreground="White"
                            Text="{Binding CurrentLine}" />
                        <!--  должно меняться во время выполнения LoadDataInDB()  -->
                        <TextBlock
                            Margin="0,0,5,0"
                            Foreground="White"
                            Text="/" />
                        <TextBlock
                            Margin="0,0,10,0"
                            Foreground="White"
                            Text="{Binding MaxLines}" />
                    </DockPanel>
    
                </Grid>
            </DockPanel>
        </Grid>
    </Window>
    Ответ написан
  • Как убрать фриз программы c# WPF?

    Casper-SC
    @Casper-SC
    .NET программист
    Ты для авторизации создаёшь целый поток. Почему тормоза тебе уже сказали, ты вызываешь authVK.join() в основном потоке и тем самым его сам же стопоришь, пока не выполнится второй поток. Вообще благодаря этому коду ты тратишь больше ресурсов и вызываешь ещё большие тормоза, если бы вообще не юзал поток.

    В данном случае использовать нужно Task. Или ещё уйму способов выполнить код не в основном потоке и не создавать новый. Почитай про пул потоков и задачи (Task).

    Я смотрю ты создал константу Empty с пустой строкой!? Так есть же string.Empty стандартный.

    <Window x:Class="Wpf_Task.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow"
            Width="525"
            Height="350"
            WindowStartupLocation="CenterScreen">
       <Window.Resources>
          <Storyboard x:Key="OnLoaded1"
                      RepeatBehavior="Forever">
             <DoubleAnimationUsingKeyFrames Storyboard.TargetName="border"
                                            Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
                <EasingDoubleKeyFrame KeyTime="0"
                                      Value="0" />
                <EasingDoubleKeyFrame KeyTime="0:0:1"
                                      Value="360" />
             </DoubleAnimationUsingKeyFrames>
          </Storyboard>
       </Window.Resources>
    
       <Window.Triggers>
          <EventTrigger RoutedEvent="FrameworkElement.Loaded">
             <BeginStoryboard Storyboard="{StaticResource OnLoaded1}" />
          </EventTrigger>
       </Window.Triggers>
    
       <Grid>
    
          <Button x:Name="executeButton"
                  Width="75"
                  Margin="0,216,0,0"
                  HorizontalAlignment="Center"
                  VerticalAlignment="Top"
                  Click="Button_Click"
                  Content="Выполнить"
                  TextOptions.TextFormattingMode="Display" />
    
          <Rectangle x:Name="border"
                     Width="100"
                     Height="100"
                     Margin="208,57,208,161"
                     HorizontalAlignment="Center"
                     VerticalAlignment="Center"
                     RenderTransformOrigin="0.5,0.5">
             <Rectangle.Fill>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                   <GradientStop Offset="0" Color="#FFD1BD69" />
                   <GradientStop Offset="1" Color="#FFFFF5C9" />
                </LinearGradientBrush>
             </Rectangle.Fill>
             <Rectangle.RenderTransform>
                <TransformGroup>
                   <ScaleTransform />
                   <SkewTransform />
                   <RotateTransform />
                   <TranslateTransform />
                </TransformGroup>
             </Rectangle.RenderTransform>
          </Rectangle>
    
          <TextBlock x:Name="_result"
                     Margin="0,187,0,0"
                     HorizontalAlignment="Center"
                     VerticalAlignment="Top"
                     Text="..."
                     TextOptions.TextFormattingMode="Display"
                     TextWrapping="Wrap" />
    
       </Grid>
    </Window>


    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows;
    
    namespace Wpf_Task
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                executeButton.IsEnabled = false;
                Task<int>.Factory.StartNew(() =>
                {
                   return Auth.Login();
                })
                .ContinueWith(task => //Выполнить код в основном потоке (TaskScheduler.FromCurrentSynchronizationContext())
                {
                    executeButton.IsEnabled = true;
                    _result.Text = task.Result.ToString();
                }, TaskScheduler.FromCurrentSynchronizationContext());
            }
        }
    
        public static class Auth
        {
            private static int _counter;
    
            public static int Login()
            {
                Thread.Sleep(1000);
                return ++_counter;
            }
        }
    }
    Ответ написан
  • Просто убийственно тормозит XAML дизайнер после добавления Autofac. Кто-нибудь с такой проблемой сталкивался?

    Casper-SC
    @Casper-SC Автор вопроса
    .NET программист
    Обновлено: 10.07.2015
    В общем решил проблему так. В конструкторе ViewModelLocator почти ничего не выполняю в режиме дизайнера кода, а свойство сделал таким:
    public MainViewModel Main
            {
                get
                {
                    return !ViewModelBase.IsInDesignModeStatic ?
                        _container.Resolve<MainViewModel>() :
                        _designerMainVm ?? (_designerMainVm = 
                        new MainViewModel(null, null, null, null) { Settings = new AppSettings() });
                }
            }

    Конец обновленного текста

    ViewModelLocator.cs
    public class ViewModelLocator : ViewModelLocatorBase
        {
            private readonly IContainer _container;
            private static ViewModelLocator _locator;
    
            public ViewModelLocator()
            {
                _locator = this;
    
                if (!ViewModelBase.IsInDesignModeStatic)
                {
                    Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
                    Application.Current.MainWindow.Loaded += MainWindow_Loaded;
                    Application.Current.MainWindow.Closing += MainWindow_Closing;
                }
    
                LogManager.Logger.LogPath = LocalConfiguration.Instance.Folders.Logs;
    
                DispatcherHelper.Initialize();
                CorrelationsLocator.Initialize();
    
                if (!ViewModelBase.IsInDesignModeStatic)
                {
                    ProgramIdentification.Initialize(ProgramIdentifier.CbpOperator);
    
                    LocalConfiguration.Instance.Folders.CheckFolders();
                    LocalConfiguration.Instance.LoadAppConfig();
                    LocalConfiguration.Instance.LoadLocalSettings();
    
                    WpfSingleInstance.AppIsAlreadyRunning += (sender, args) =>
                        MessageBox.Show(string.Format("Приложение: {0} уже запущено", args.AppName));
                    WpfSingleInstance.Make();
                }
                _container = RegisterDependencies(LocalConfiguration.Instance);
            }
    
            private IContainer RegisterDependencies(LocalConfiguration config)
            {
                var builder = new ContainerBuilder();
    
                builder.RegisterType<LauncherViewModel>().SingleInstance();
                builder.RegisterType<ShutdownViewModel>().SingleInstance();
                builder.RegisterType<CommandManager>().SingleInstance();
                builder.RegisterType<AccountControllerVm>().SingleInstance();
                builder.RegisterType<ComponentEditorViewModel>().SingleInstance();
                builder.RegisterType<ComponentSelectorViewModel>().SingleInstance();
                builder.RegisterType<RecipeTimeSetEditorViewModel>().SingleInstance();
                builder.RegisterType<CategoryEditorViewModel>().SingleInstance();
                builder.RegisterType<AboutDialogViewModel>().SingleInstance();
                builder.RegisterType<L1ApplicationManagerViewModel>().SingleInstance();
    
                builder.RegisterType<CarEditorViewModel>() //Редактор машин
                    .OnActivated(e =>
                    {
                        e.Instance.WindowSettings = config.Settings.CarsWindowSettings; //Настройки окна (положение, размер, состояние)
                    }).SingleInstance();
    
                builder.RegisterType<ClientsEditorViewModel>() //Редактор клиентов
                    .OnActivated(e =>
                    {
                        e.Instance.WindowSettings = config.Settings.ClientsWindowSettings; //Настройки окна (положение, размер, состояние)
                    }).SingleInstance();
    
                builder.RegisterType<ReportsWindowViewModel>() //Отчёты
                    .OnActivated(e =>
                    {
                        e.Instance.WindowSettings = config.Settings.ReportsWindowSettings; //Настройки окна (положение, размер, состояние)
                        e.Instance.ReportsFolder = config.Folders.Documents;
                        e.Instance.ReportMode = ReportMode.Applications;
                    });
    
                builder.RegisterType<EventLogViewModel>() //Журнал событий
                    .OnActivated(e =>
                    {
                        e.Instance.WindowSettings = config.Settings.EventLogWindowSettings; //Настройки окна (положение, размер, состояние)
                        e.Instance.SetConfiguration(config.Config, config.Folders.EventReportFolder);
                    });
    
                builder.RegisterType<RecipesEditorViewModel>() //Редактор рецептов
                    .OnActivated(e =>
                    {
                        e.Instance.WindowSettings = config.Settings.RecipeWindowSettings; //Настройки окна (положение, размер, состояние)
                        e.Instance.SetEditorSettings(config.Settings.RecipeEditorConfig);
                    })
                    .SingleInstance();
    
                builder.RegisterType<ApplicationsEditorViewModel>() //Редактор заявок
                    .OnActivated(e =>
                    {
                        e.Instance.LastSelectedItems = config.Settings.LastSelectedItemsInAppWindow; //Состояние последних выбранных элементов.
                        e.Instance.WindowSettings = config.Settings.AppEditorWindowSettings;
                    })
                    .SingleInstance();
    
                builder.RegisterType<MainViewModel>() 
                    .OnActivated(e =>
                    {
                        var appManager = e.Context.Resolve<L1ApplicationManagerViewModel>();
                        appManager.Main = e.Instance;
                        if (!ViewModelBase.IsInDesignModeStatic)
                        {
                            e.Instance.Title = ProgramIdentification.GetCurrentProgramName();
                        }
                        e.Instance.Settings = config.Settings.ApplicationSettings;
                    })
                    .SingleInstance();
    
                return builder.Build();
            }
    
            private void MainWindow_Loaded(object sender, RoutedEventArgs e)
            {
                WindowManager.Instance.Initialize(Application.Current.MainWindow, Main);
                Main.OnWindowLoaded();
            }
    
            private void MainWindow_Closing(object sender, CancelEventArgs e)
            {
                Debug.WriteLine("Начало: MainWindow_Closing");
                Window window = (Window)sender;
    
                Debug.WriteLine("Конец: MainWindow_Closing");
    
                if (WindowManager.Instance.OpenMessageDialog(DialogIcons.Help, DialogButtons.YesNo,
                    "Выход из программы",
                    "Вы действительно хотите завершить работу приложения?") == UserDialogResult.No)
                    e.Cancel = true;
                else
                {
                    window.Closing -= MainWindow_Closing;
                    try
                    {
                        LocalConfiguration.Instance.SaveLocalSettings();
                        LocalConfiguration.Instance.SaveCurrentAppConfig();
                    }
                    catch (Exception ex)
                    {
                        LogManager.Logger.AppendException(ex);
                    }
                }
            }
    
            private void Current_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
            {
                LogManager.Logger.AppendException(e.Exception);
                MessageBox.Show(e.Exception.ToString());
            }
    
            #region Свойства
    
            public static ViewModelLocator Current
            {
                get { return _locator; }
            }
    
            public MainViewModel Main
            {
                get { return _container.Resolve<MainViewModel>(); }
            }
    
            public L1ApplicationManagerViewModel ApplicationsManager
            {
                get { return _container.Resolve<L1ApplicationManagerViewModel>(); }
            }
    
            public AccountControllerVm AccountController
            {
                get { return _container.Resolve<AccountControllerVm>(); }
            }
    
            public ShutdownViewModel Shutdown
            {
                get { return _container.Resolve<ShutdownViewModel>(); }
            }
    
            public CategoryEditorViewModel CategoryEditor
            {
                get { return _container.Resolve<CategoryEditorViewModel>(); }
            }
    
            public ApplicationsEditorViewModel ApplicationsEditor
            {
                get { return _container.Resolve<ApplicationsEditorViewModel>(); }
            }
    
            public RecipesEditorViewModel RecipesEditor
            {
                get { return _container.Resolve<RecipesEditorViewModel>(); }
            }
    
            public ComponentEditorViewModel ComponentEditor
            {
                get { return _container.Resolve<ComponentEditorViewModel>(); }
            }
    
            public ComponentSelectorViewModel ComponentsSelector
            {
                get { return _container.Resolve<ComponentSelectorViewModel>(); }
            }
    
            /// <summary>Редактор клиентов</summary>
            public ClientsEditorViewModel ClientsEditor
            {
                get { return _container.Resolve<ClientsEditorViewModel>(); }
            }
    
            public CarEditorViewModel CarsEditor
            {
                get { return _container.Resolve<CarEditorViewModel>(); }
            }
    
            public CommandManager CommonCommands
            {
                get { return _container.Resolve<CommandManager>(); }
            }
    
            public LauncherViewModel Launcher
            {
                get { return _container.Resolve<LauncherViewModel>(); }
            }
    
            public AboutDialogViewModel About
            {
                get { return _container.Resolve<AboutDialogViewModel>(); }
            }
    
            /// <summary>
            /// Возвращает новый экземпляр модели представления Отчётов
            /// </summary>
            public ReportsWindowViewModel NewReport
            {
                get { return _container.Resolve<ReportsWindowViewModel>(); }
            }
    
            /// <summary>
            /// Возвращает новый экземпляр модели представления Журнала событий
            /// </summary>
            public EventLogViewModel NewEventLog
            {
                get { return _container.Resolve<EventLogViewModel>(); }
            }
    
    
            #endregion
    
            #region Методы
    
    
            #endregion
        }
    Ответ написан
  • Как запретить остановку программы?

    Casper-SC
    @Casper-SC
    .NET программист
    Обычно, когда рассказывают, что что-то не работает, прикладывают код. Сидел и думал, а причём тут WPF.
    Ответ написан
  • Как правильно написать на MVVM?

    Casper-SC
    @Casper-SC
    .NET программист
    Если актуалити, то могу показать пример с библиотекой MvvmLight.
    Ответ написан
  • Как удалить определенный Image с Canvas?

    Casper-SC
    @Casper-SC
    .NET программист
    Ну вот, к примеру:
    private void DoWork()
    {
        Canvas CnMapField = new Canvas();
    
        Image[] images = CopyFrom<Image>(CnMapField.Children);
        foreach (Image img in images)
        {
            if (img.Margin.Left == x)
                CnMapField.Children.Remove(img);
        }
    }
    
    private T[] CopyFrom<T>(UIElementCollection collection) where T : UIElement
    {
        var images = new List<T>(collection.Count);
        images.AddRange(collection.Cast<T>());
        return images.ToArray();
    }


    То есть ты foreach(ем) бегаешь по копии коллекции, а удаляешь из оригинала.

    Хотя в данном случае приведение к Image не обязательно.
    Ответ написан