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

    @Sumor
    Практическое руководство. Фоновое выполнение операции

    Для выполнения продолжительной задачи нужно использовать BackgroundWorker.
    В событии DoWork описываете вашу длительную работу.
    В процессе работы (в обработчике DoWork) вызываете метод ReportProgress, чтобы сообщить в основную программу о состоянии обработки. В основную программу приходит событие ProgressChanged.
    Периодически отслеживаете не пытается ли кто остановить вашу работу через CancellationPending.

    Для запуска работы используете метод RunWorkerAsync. Через событие ProgressChanged отслеживаете прогресс и обновляете прогрессбар. По событию RunWorkerCompleted узнаёте о завершении или прерывании работы.
    Ответ написан
    1 комментарий
  • Как сгруппировать данные в Observalble Collection и привязать их к DataGrid (C# + WPF)?

    @Sumor
    Если вам нужно сгруппировать исходные данные (показать по группам). То для этого есть механизм группировки, встроенной в WPF. Пример его использования описан в MSDN. Основная суть - вы описываете описываете CollectionViewSource, который в качестве источника берёт исходные данные, а затем в CollectionViewSource указываете свойство, по которому группировать. В DataGrid на входе указывается созданный CollectionViewSource. Дополнительно можно описать шаблон представления именно группированных данных.

    Если же вы хотите отображать именно список StatisticsModel. То для этого всего лишь надо в динамике присвоить свойству Source объекта DataGrid результат вашей последней конструкции this.statisticsCollection.GroupBy(...).Select(...)
    Ответ написан
    3 комментария
  • Почему не открывается диалог WPF?

    @Sumor
    В целом, то что вы закрыли диалог совершенно не означает, что ваша длительная операция прервалась.
    backgroundWorker.CancelAsync(); всего лишь информирует задачу, что её прерывают. Внутри обработчика backgroundWorker должна быть специальная проверка свойства CancellationPending, при установке которого нужно выполнять выход из потока обработки.
    Закрытие формы не уничтожает поток с задачей - она будет выполняться, пока не дойдёт до конца.
    Скорее всего при повторном открытии формы у вас происходит конфликт с ресурсами, которые уже используются первым потоком обработки.

    Если смотреть дальше, то если вы выполняете длительную операцию на сервере, например, длительную хранимую процедуру, то даже закрытие приложения, которое запустило на выполнение эту хранимку, не гарантирует остановку процесса на сервере. Особенно, если процесс изменяет данные. В зависимости от логики сервера процесс может продолжиться до победного конца, может прерваться в какой-нибудь ключевой момент или уйти в длительный откат изменений, чтобы привести всё в состояние до запуска хранимки.
    Ответ написан
    Комментировать
  • Как TextBox и компорт сделать в одном потоке?

    @Sumor
    Нужно переключиться, по необходимости, в поток, которому принадлежит TextBox.
    Для проверки используется функция CheckAccess(). Её нет в Intellisence (то есть в автодобавлении), но она нормально компилируется.
    Если вы находитесь не в нужном потоке, то для вызова кода в нужном используется функция Invoke, от объекта Dispatcher того объекта, в потоке которого нужно выполнить код.
    Код примерно такой (возможны синтаксические ошибки - нет под рукой студии):
    void AddText(string message)
    {
       if (!textbox.CheckAccess())
       {
           textbox.Dispatcher.Invoke(new Action<string>(AddText), message);
       }
       else
       {
           textbox.AppendText(message);
       }   
    }
    Ответ написан
    5 комментариев
  • Как сделать динамический список CheckBox в WPF ?

    @Sumor
    В простейшем случае это должно выглядеть где-то так:
    XAML:
    <ListBox x:Name="lst">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <CheckBox Content="{Binding Value}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    В коде, например:
    lst.ItemsSource = new List<KeyValuePair<int, string>>()
    {
        new KeyValuePair<int, string>(1, "1"),
        new KeyValuePair<int, string>(2, "2"),
    };

    Данный код покажет вам ваш список с чекбоксиками. К чекбоксу можно привязать событие на изменение и отлавливать какой элемент зачекили или расчекили.
    При желании можно получить список отмеченных, но это не так тривиально.

    Лучше использовать для отображения объект у которого есть логическое свойство, например IsChecked, и его привязать к IsChecked CheckBox.
    Например:
    Класс:
    class MyClass
    {
        public int id { get; set; }
        public string Name { get; set; }
        public bool IsChecked { get; set; }
    }

    XAML:
    <ListBox x:Name="lst">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    Добавление элементов:
    lst.ItemsSource = new List<MyClass>()
    {
        new MyClass(){id=1, Name="1"},
        new MyClass(){id=2, Name="2"},
    };

    В таком случае отметка пользователем галочки сразу отражается в привязанном списке и можно легко получить список отмеченных:
    foreach(var tObj in (lst.ItemsSource as List<MyClass>).Where(myObj => myObj.IsChecked))
        MessageBox.Show(tObj.Name);
    Ответ написан
    2 комментария
  • Как передать в параметр ссылку, с помощью xaml?

    @Sumor
    Может стоит отказаться от конструкторов с параметрами и задавать параметры свойствами.
    Примерно так:
    <ObjectDataProvider x:Key="userDCustom" ObjectType="{x:Type local:UserData}">
                <ObjectDataProvider.ConstructorParameters>
                    <local:User>
                          <local:User.UserData>
                                 <local.User Name="MyName" />
                          </local:User.UserData>
                    </local:User>
                </ObjectDataProvider.ConstructorParameters>
    </ObjectDataProvider>
    Ответ написан
    4 комментария
  • Как приостановить туннелирование события в WPF?

    @Sumor
    В вашем случае лучше использовать сканер в режиме работы com-порта.
    Либо изменить логику программы так, чтобы сканер просто заменял клавиатуру, как это и сделано, например, в торговых точках — там данные можно ввести со сканера, а можно набить руками.

    Что касается введённых символов, то вы их можете все запоминать и все не пропускать, а после того как разберётесь что с ними делать ввод данных можно эмулировать через SendInput. Но этот путь некрасив, ведёт к ошибкам, особенно при использовании спецсимволов или быстрых клавиш.
    Ответ написан
    Комментировать
  • Как в WPF создать кнопку, при наведении на которую, под текстом появилась горизонтальная черта?

    @Sumor
    В качестве промежуточного решения может подойти следующее.
    <Button >            
        <Run>
            <Run.Style>
                <Style TargetType="Run">
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="TextDecorations" Value="Underline" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </Run.Style>
            Текст
        </Run>
    </Button>
    Ответ написан
    2 комментария
  • WPF "проглатывает" исключения. Куда копать?

    @Sumor
    Ошибки Binding проглатываются — это действительно фишка WPF. Они пишутся в выход отладчика с описанием ошибки.
    Для отлова ошибок можно использовать событие BindingComplete, примерно, как это описано у Microsoft.
    Пример отлова ошибок Binding.

    Помимо этого есть интересный механизм проверки значений ValidationRule.
    Пример использования ValidationRule
    Ответ написан
    1 комментарий
  • Как создать свой элемент в visual stidio?

    @Sumor
    Для этого вы можете создать UserControl или CustomControl.
    В первом случае вы создаёте UserControl (пользовательский элемент управления) добавляя его к проекту как окно приложения. Настраиваете по сути как часть окна приложения добавляя нужные контролы на него и описывая логику в коде. После компиляции проекта контрол автоматически попадает на панель инструментов.
    Например:
    <UserControl x:Class="WpfApplication3.UserControl1"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBox />
            <ListBox Grid.Row="1" />
        </Grid>
    </UserControl>


    Во втором случае вы как бы наследуетесь от базового контрола и добавляете к тему оформление и немного логики. В меню добавления элементов этот тип называется CustomControl или настраиваемый элемент управления.
    Либо через C#:
    public MyTextBox : TextBox
    {
    
    }


    Либо через Xaml:
    <TextBox x:Class="WpfApplication1.MyTextBox"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" >
       
    </TextBox>
    Ответ написан
    Комментировать
  • В чем ошибка при связывании (binding'е) данных?

    @Sumor
    Для TwoWay нужно указывать с использованием Path или XPath.

    Попробуй так:
    //this = currentObject; 
    //typeof(this) == typeof(Reader);
    
    var indexBinding = new Binding();
    indexBinding.Mode = BindingMode.TwoWay;
    //указываю подчненный контролл как источник данных
    indexBinding.ElementName = "epubConrol";
    indexBinding.Path = new PropertyPath("SelectedIndex");
    //Очищаю свойство зависимостей и создаю привязку 
    currentObject.ClearValue(Reader.SelectedIndexProperty);
    currentObject.SetBinding(Reader.SelectedIndexProperty, indexBinding);


    На Xaml это будет выглядеть примерно так:
    <ListBox x:Name="lst1" SelectedIndex="{Binding ElementName=lst2, Path=SelectedIndex, Mode=TwoWay}">
        <system:String>1 строка</system:String>
        <system:String>2 строка</system:String>
    </ListBox>
    <ListBox Grid.Column="1" x:Name="lst2">
        <system:String>1 строка</system:String>
        <system:String>2 строка</system:String>
    </ListBox>
    Ответ написан
    4 комментария
  • Как осуществить нестандартную привязку данных к DataGrid в WPF?

    @Sumor
    Предлагаю воспользоваться не датагридом, а ItemsControl для вывода списка ваших параметров.
    Внешний ItemsControl перечисляет свойство класса типа List, второй ItemsControl — элемент этого списка типа byte[][], третий — byte[] и отображает TextBox для каждой цифры.
    За счёт связывания (Binding) вы меняя значения в текстбоксах, меняете его в свойстве вашего объекта.

    Xaml:
    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <ItemsControl x:Name="itemsData">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <ItemsControl BorderBrush="Black" BorderThickness="1" ItemsSource="{Binding}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <ItemsControl BorderBrush="Black" BorderThickness="1" ItemsSource="{Binding}">
                                        <ItemsControl.ItemsPanel>
                                            <ItemsPanelTemplate>
                                                <StackPanel  Orientation="Horizontal"/>
                                            </ItemsPanelTemplate>
                                        </ItemsControl.ItemsPanel>
                                        <ItemsControl.ItemTemplate>
                                            <DataTemplate>
                                                <TextBox Text="{Binding Path=.}" />
                                            </DataTemplate>
                                        </ItemsControl.ItemTemplate>
                                    </ItemsControl>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Grid>
    </Window>


    C#
    namespace WpfApplication1
    {
        /// <summary>
        /// Логика взаимодействия для MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                var my = new MyClass();
                my.Data = new List<byte[][]>();
                my.Data.Add(new byte[][] { new byte[] { 0, 1 }, new byte[] { 2, 3 }, new byte[] { 4, 5 } });
                my.Data.Add(new byte[][] { new byte[] { 6, 7 }, new byte[] { 8, 9 }, new byte[] { 10, 11 } });
                itemsData.ItemsSource = my.Data;
            }
    
        }
    
        public class MyClass
        {
            public List<byte[][]> Data
            { get; set; }
        }
    }
    Ответ написан
    Комментировать
  • Как поменять свойство родительского контрола?

    @Sumor
    Поставь у скрола свойство IsHitTestVisible в false. Тогда оно будет пропускать события от мыши сквозь себя и радиобатон нормально переключается.
    <ScrollViewer VerticalScrollBarVisibility="Auto" IsHitTestVisible="False" >
           <TextBlock TextWrapping="Wrap" Text="{Binding FirstInformation}"/>
    </ScrollViewer>


    Если есть желание возиться с событиями, то можно предложить такой вариант (но я бы так не стал делать).
    <ScrollViewer VerticalScrollBarVisibility="Auto" >
           <TextBlock TextWrapping="Wrap" Text="{Binding FirstInformation}"  MouseLeftButtonUp="TextBlock_MouseLeftButtonUp"/>
    </ScrollViewer>
    
    private void TextBlock_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
                var tb = sender as TextBlock;
                // Нужно дополнительно обложиться проверками, что типы совпадают
                ((tb.Parent as ScrollViewer).Parent as RadioButton).IsChecked = true;            
    }
    Ответ написан
    Комментировать
  • WPF Доступ к объектам другой страницы

    @Sumor
    Из вопроса неочевидно как всё-таки связаны Page1 и Page2.
    Если из Page1 вызывается Page2, то самый простой способ при вызове создать экземпляр Page2 и присвоить значение переменной.
    Navigate(new Page2(){ variable = "abc"});
    Можно использовать для передачи объект состояния перехода — дополнительный параметр метода Navigate. Пример из MSDN:
    void goButton_Click(object sender, RoutedEventArgs e)
    {
        this.NavigationService.Navigate(new ContentPage(), DateTime.Now);
    }
    void NavigationService_LoadCompleted(object sender, NavigationEventArgs e)
    {
        DateTime requestDateTime = (DateTime)e.ExtraData;
        string msg = string.Format("Request started {0}\nRequest completed {1}", requestDateTime, DateTime.Now);
        MessageBox.Show(msg);
    }


    Ну и самый нерекомендуемый способ - использование статических свойств для передачи состояния.
    Ответ написан
    2 комментария
  • Не могу связать ObservableCollection с ListView (WPF)?

    @Sumor
    Скорее всего у вас некорректно задан Binding у ListView.
    Попробуйте явно задать, например, в конструкторе после инициализации:
    tProfiler.ItemsSource = queryInf;
    Всё должно получиться.
    Ответ написан
  • Как средствами WPF отрисовать график функции, используя Canvas?

    @Sumor
    Вычисляются значения функции с определённым шагом, дальше в колекцию PathFigure добавляются объекты типа LineSegment (если не нужно интерполировать) или другой наследник класса PathSegment, соответствующий нужной интерполяции. Например, кривыми Безье третьего порядка — PolyBezierSegment.
    Point указывает новую точку, до которой нужно продолжить график.
    При расчётах нужно учитывать, что у Canvas начало координат находится в левом верхнем углу, а ось Y направлена вниз. Либо учитывать при расчётах следующей точки, либо использовать трансформации Canvas.
    Должно получиться вроде этого
    <Canvas>
        <Path Stroke="Black" StrokeThickness="1">
            <Path.Data>
                <PathGeometry>
                    <PathFigure>
                        <LineSegment Point="100,100"/>
                        <LineSegment Point="150,120"/>      
                        <PolyBezierSegment Points="170,120 190,100 200,160 220,140"/>
                    </PathFigure>
                </PathGeometry>
            </Path.Data>
        </Path>    
    </Canvas>
    Ответ написан
    3 комментария