Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.63/8: Рейтинг темы: голосов - 8, средняя оценка - 4.63
0 / 0 / 0
Регистрация: 03.09.2022
Сообщений: 69

Вложенные View элементы. Взаимодействие с ViewModel

04.03.2024, 14:49. Показов 2447. Ответов 47

Студворк — интернет-сервис помощи студентам
Задача: изначально неизвестно количество expander и button внутри него. Они будут извлекаться из JSON файла и отображаться во view (позже добавится кнопка обновления данных, в случае каких-либо изменений во время выполнения программы). Предварительная их инициализация содержится в модельном классе ниже. Можно ли сделать шаблон через xml или всё же необходимо будет использовать код в данном случае? В будущем, вероятно возникнет вложенность ещё одного expander, но думаю, всё будет по аналогии...
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
 
namespace Tests
{
    public class ButtonsCreator
    {
        ObservableCollection<SomeData> SomeData { get; } = new();
        public ObservableCollection<SomeData> Create()
        {
            Random random = new();
            var tu = random.Next(0, 5);
            for (int i = 0; i < tu; i++)
            {
                SomeData.Add(new SomeData("Я кнопка",  @"\\content"));
            }
            return SomeData;
        }
 
    }
C#
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    public class ExpanderCreator
    {
        public ObservableCollection<ExpanderData> ExpanderData { get;} = new();
        public ObservableCollection<SomeData> someDatas = new();
        public ExpanderCreator()
        {
            ButtonsCreator buttonsCreator = new();
            Random random = new();
            var randomNumber = random.Next(0, 5);
            for (int i = 0; i < randomNumber; i++)
            {
                someDatas = buttonsCreator.Create();
                ExpanderData.Add(new ExpanderData(someDatas, "Expanderinger"));
            }
        }
    }
C#
44
45
46
47
48
49
50
51
52
53
54
    public class ExpanderData
    {
        public ObservableCollection<SomeData> SomeData { get; } = new();
        public string? HeaderExpander { get; set; }
        public ExpanderData() { }
        public ExpanderData(ObservableCollection<SomeData> someData, string? headerExpander)
        {
            SomeData = someData;
            HeaderExpander = headerExpander;
        }
    }
C#
55
56
57
58
59
60
61
62
63
64
65
66
67
68
    public class SomeData
    {
        public string? NameButton { get; set; }
        public string? UrlButton { get; set; }
 
        public SomeData() { }
 
        public SomeData(string? nameButton, string? urlButton)
        {
            NameButton = nameButton;
            UrlButton = urlButton;
        }
    }
}
Ниже представление накидано на скорую руку
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<Window x:Class="Tests.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:local="clr-namespace:Tests"
        mc:Ignorable="d"
        Title="MainWindow" 
        Height="700"
        MinHeight="700" 
        Width="238"
        MinWidth="238">
    <FrameworkElement.Resources>
        <DataTemplate x:Key="expandleTemplate"
                      DataType="{x:Type local:ExpanderData}">
            <Expander Header="{Binding HeaderExpander}"
                      Background="Purple">
                <DataTemplate DataType="{x:Type local:SomeData}">
                    <Button Margin="10" Padding="15 5" Name="{Binding NameButton}" Command=""/>
                </DataTemplate>
            </Expander>
        </DataTemplate>
    </FrameworkElement.Resources>
    <UniformGrid Columns="2">
        <ItemsControl ItemsSource="{Binding ExpanderData}"
                      ItemTemplate="{DynamicResource expandleTemplate}">
        </ItemsControl>
    </UniformGrid>
</Window>
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
04.03.2024, 14:49
Ответы с готовыми решениями:

Взаимодействие View и ViewModel
Здравствуйте! Пытаюсь разобраться с паттерном MVVM. Сделал тренировочный проект, и в ViewModel к Model я обратиться могу(получаю данные,...

Паттерн MVVM. Взаимодействие ViewModel с View
Пишу приложение, используя паттерн mvvm. Есть солюшн, и в нем три проекта. %appname%View, %appname%ViewModel, %appname%Model. Ну и...

Как зарегистрировать ViewModel через Autofac, создав между ViewModel и нужным View DataContext связь?
Доброго времени суток. Есть желание при регистрации ViewModel'и сразу же &quot;подключиться&quot; к DataContext'у у нужной View, как например: ...

47
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16158 / 11278 / 2891
Регистрация: 21.04.2018
Сообщений: 33,164
Записей в блоге: 2
05.03.2024, 01:24
Лучший ответ Сообщение было отмечено Dmitry12329 как решение

Решение

Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Dmitry12329 Посмотреть сообщение
Не подскажите, а что нужно исправить в коде?
В первую очередь, лучше перейти от иерархической структуры, к линейной (реляционной).
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    public class ButtonData
    {
        public int Id { get; set; }
        public string? NameButton { get; set; }
        public string? UrlButton { get; set; }
 
        public int ExpanderDataId { get; set; }
 
        public ButtonData() { }
 
        public ButtonData(string? nameButton, string? urlButton)
        {
            NameButton = nameButton;
            UrlButton = urlButton;
        }
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
    public class ExpanderData
    {
        public int Id { get; set; }
 
        //public ObservableCollection<SomeData> SomeData { get; } = new();
        public string? HeaderExpander { get; set; }
        public ExpanderData() { }
        public ExpanderData(/*ObservableCollection<SomeData> someData,*/ string? headerExpander)
        {
            //SomeData = someData;
            HeaderExpander = headerExpander;
        }
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    public static class CollectionHelper
    {
        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> colection, Action<T> action)
        {
            foreach (T item in colection)
            {
                action(item);
                yield return item;
            }
        }
        public static ICollection<T> AddRange<T>(this ICollection<T> colection, IEnumerable<T> items)
        {
            foreach (T item in items)
                colection.Add(item);
            return colection;
        }
 
        public static T RandomElement<T>(this IReadOnlyList<T> list)
            => list[Random.Shared.Next(list.Count)];
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
    public static class ButtonsCreator
    {
        private static int id;
        public static IEnumerable<ButtonData> Create()
        {
            Random random = new();
            var tu = random.Next(5, 15);
            for (int i = 0; i < tu; i++)
            {
                yield return new ButtonData($"Я кнопка #{id++}", @"\\content");
            }
        }
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    public class ExpanderCreator
    {
        public ObservableCollection<ExpanderData> ExpandersCollection { get; } = new();
        public ObservableCollection<ButtonData> ButtonsCollection { get; } = new();
        public ExpanderCreator()
        {
            var randomNumber = Random.Shared.Next(2, 6);
            for (int i = 1; i <= randomNumber; i++)
            {
                ExpandersCollection.Add(new($"Я булочка №{i}") { Id = i });
            }
            ButtonsCollection.AddRange(ButtonsCreator.Create().ForEach(b => b.ExpanderDataId = ExpandersCollection.RandomElement().Id));
        }
    }
Добавлено через 17 минут
CollectionViewSource для фильтрации по ExpanderDataId:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    public class ButtonsForExpanderViewSource : CollectionViewSource
    {
 
        public ButtonsForExpanderViewSource()
        {
            IsLiveFilteringRequested = true;
            LiveFilteringProperties.Add(nameof(ButtonData.ExpanderDataId));
            filterHandler = (_, args) => args.Accepted = args.Item is ButtonData;
            Filter += filterHandler;
        }
 
        public int? Id
        {
            get { return (int)GetValue(IdProperty); }
            set { SetValue(IdProperty, value); }
        }
 
        // Using a DependencyProperty as the backing store for Id.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IdProperty =
            DependencyProperty.Register("Id", typeof(int?), typeof(ButtonsForExpanderViewSource),
                new PropertyMetadata((int?)null, OnIdChanged));
 
        private FilterEventHandler filterHandler;
 
        private static void OnIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ButtonsForExpanderViewSource viewSource = (ButtonsForExpanderViewSource)d;
            viewSource.Filter -= viewSource.filterHandler;
            if (e.NewValue is int id)
            {
                viewSource.filterHandler = (_, args) => args.Accepted = args.Item is ButtonData data && data.ExpanderDataId == id;
            }
            else
            {
                viewSource.filterHandler = (_, args) => args.Accepted = args.Item is ButtonData;
            }
            viewSource.Filter += viewSource.filterHandler;
        }
    }
Добавлено через 26 минут
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<Window x:Class="Core2024.CF.Dmitry12329.Topic3157580.ExpandersWindow"
        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:local="clr-namespace:Core2024.CF.Dmitry12329.Topic3157580"
        mc:Ignorable="d"
        Title="ExpandersWindow"  Height="700"
        MinHeight="700" 
        Width="238"
        MinWidth="238"
        DataContext="{DynamicResource vm}">
    <FrameworkElement.Resources>
        <local:ExpanderCreator x:Key="vm"/>
        <DataTemplate x:Key="someDataTemplate" DataType="{x:Type local:ButtonData}">
            <Button Margin="10" Padding="15 5" Content="{Binding NameButton}" Command=""/>
        </DataTemplate>
        <DataTemplate x:Key="expandleTemplate"
                      DataType="{x:Type local:ExpanderData}">
            <Expander Header="{Binding HeaderExpander}"
                      Background="Purple">
                <FrameworkElement.Resources>
                    <local:ButtonsForExpanderViewSource x:Key="viewSource"
                                                        Id="{Binding Id}"
                                                        Source="{Binding ButtonsCollection, Source={StaticResource vm}}"/>
                </FrameworkElement.Resources>
                <ItemsControl ItemsSource="{Binding Mode=OneWay, Source={StaticResource viewSource}}"
                              ItemTemplate="{StaticResource someDataTemplate}"/>
            </Expander>
        </DataTemplate>
    </FrameworkElement.Resources>
    <UniformGrid >
        <ItemsControl ItemsSource="{Binding ExpandersCollection}"
                      ItemTemplate="{DynamicResource expandleTemplate}">
        </ItemsControl>
    </UniformGrid>
</Window>
1
0 / 0 / 0
Регистрация: 03.09.2022
Сообщений: 69
05.03.2024, 13:40  [ТС]
Элд Хасп, а для чего нам нужна фильтрация по ExpanderDataId? (да и вообще, для чего нужна фильтрация? Просто с ней не сталкивался ещё). А также для чего нам нужны Id на кнопках (для номинации кнопок, как я понял) и expander?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16158 / 11278 / 2891
Регистрация: 21.04.2018
Сообщений: 33,164
Записей в блоге: 2
05.03.2024, 15:36
Цитата Сообщение от Dmitry12329 Посмотреть сообщение
а для чего нам нужна фильтрация по ExpanderDataId?
Чтобы из обшей коллекции ButtonsCollection выбрать элементы относящиеся к конкретному Expander.
Это альтернативный способ формирования иерархического представления для реляционного источника.

Цитата Сообщение от Dmitry12329 Посмотреть сообщение
А также для чего нам нужны Id на кнопках
В реляционной структуре экземпляры типов (сущностей) отличаются обычно одним значащим свойством. Чаще всего это целочисленный Id.

Добавлено через 2 минуты
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Это альтернативный способ формирования иерархического представления для реляционного источника.
Плюс этого подхода - динамическое формирование. И удобство работы реляционными источниками vs иерархическими.
0
0 / 0 / 0
Регистрация: 03.09.2022
Сообщений: 69
05.03.2024, 18:01  [ТС]
Элд Хасп, хм, даже так. Просто почему-то мне казалось, что в моём случае работает это по-другому. Я извлекаю первый JSON в котором название expander, это название самого файла, а то, что внутри, это информация о кнопках, они по структуре похожи, только свойствами полей отличаются. И получается, что прочитав один файл (или прочитав всё,передать коллекцию expander), модель передаёт это VM, а VM передаëт view и так последовательно, пока все файлы не будут прочитаны. Видимо, мой подход не совсем корректен был... Но ладно, я получил информацию о view необходимую. Как всё совмещу, загружу на github и скину сюда
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16158 / 11278 / 2891
Регистрация: 21.04.2018
Сообщений: 33,164
Записей в блоге: 2
05.03.2024, 18:10
Цитата Сообщение от Dmitry12329 Посмотреть сообщение
Я извлекаю первый JSON в котором название expander,
JSON, XML - это последовательные файлы. Они не предполагают частичного изменения. Поэтому в них достаточно просто реализовать иерархическую структуру данных.
Вот только работать с ней тоже будет не просто.
Например вам нужно найти какой-то URL.
Поиск по линейной коллекции намного проще реализовать чем по дереву.
0
0 / 0 / 0
Регистрация: 03.09.2022
Сообщений: 69
05.03.2024, 18:13  [ТС]
Элд Хасп, понял. Просто мне там, по хорошему, ничего искать не придётся, просто спарсить данные и всë. Но попробую реализовать всё с учётом Ваших комментариев, а там, как выйдет... А где и что можно изучить/ознакомиться по структурам данных?
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
05.03.2024, 18:14
Цитата Сообщение от Dmitry12329 Посмотреть сообщение
И получается, что прочитав один файл (или прочитав всё,передать коллекцию expander), модель передаёт это VM, а VM передаëт view и так последовательно
Так можно сделать, это намного проще чем делать реляционный источник. В этом случае просто связи устанавливаются явно все, одна модель содержит другую и т.п и это всё отображается по вложенности.
Чем это хуже или чем лучше использовать линейную структуру не подскажу. Ибо я долгое время занимался не связанными вещами с wpf, бд и т.п. И в это не вникал пока что. Но советую посмотреть видео какое нибудь, почитать статьи сравнения обоих подходов. Если собрались с этим работать, вы обязаны и то и то сравнить хотя бы в теории)
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16158 / 11278 / 2891
Регистрация: 21.04.2018
Сообщений: 33,164
Записей в блоге: 2
05.03.2024, 18:20
Цитата Сообщение от Dmitry12329 Посмотреть сообщение
Видимо, мой подход не совсем корректен был...
Он корректен, но чаще всего более сложен в реализации.
Если есть возможность, то лучше перейти от хранения в файловой системе к БД.
К стати, обратите внимание, что файловая система - это тоже древовидное ПРЕДСТАВЛЕНИЕ физически линейного диска.

Если нет возможности перехода на БД (ну, задание такое - и ничего с этим не сделаешь), то можно, конечно, и файловую систему использовать.

В вашем случае, вы читаете все файлы какой-то папки. И каждый файл это и есть "булочка"?
А в файле информация для кнопок булочек - верно?

Добавлено через 3 минуты
Цитата Сообщение от Dmitry12329 Посмотреть сообщение
просто спарсить данные и всë. Но попробую реализовать всё с учётом Ваших комментариев, а там, как выйдет..
Если только для этого - не надо.
Если от файловой системы никуда не деться, то надо оставить дерево.

Просто по вашему примеру я этого не понял.
1
0 / 0 / 0
Регистрация: 03.09.2022
Сообщений: 69
05.03.2024, 18:21  [ТС]
Элд Хасп, в принципе, ничего не мешает взять SQLite тогда в таком случае (вероятно много позже, данные будут извлекаться из google.sheet), просто казалось, что JSON более легковесен в использовании такой небольшой задачи и логичен в использовании. Да, в парке содержатся файлы условные: булочка1.json, булочка2.json, булочкаn.json. Название их, это заголовок expander. Внутри каждой булочки содержится информация о кнопках, название и ссылка. Количество кнопок может быть разным, но по структуре они однотипны
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
05.03.2024, 18:27
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Он корректен, но чаще всего более сложен в реализации.
Если хорошо ориентироваться в структуре, то это легко. Можно сравнить со связями как многие ко многим. Легко прочитать данные, в плане отображения во view скорее всего легче линейно для каждой вложенности отображать.

Добавлено через 2 минуты
Цитата Сообщение от Dmitry12329 Посмотреть сообщение
в принципе, ничего не мешает взять SQLite тогда в таком случае (вероятно много позже, данные будут извлекаться из google.sheet), просто казалось, что JSON более легковесен в использовании такой небольшой задачи. Да, в парке содержатся файлы условные: булочка1.json, булочка2.json, булочкаn.json. Название их, это заголовок expander. Внутри каждой булочки содержится информация о кнопках, название и ссылка. Количество кнопок может быть разным, но по структуре они однотипны
Если данные хранить не нужно и они нужны только для представления. Например вы получаете их по api в json. Тогда бд для этих данных лишняя, да и вообще ответ по апи деселиризовать в памяти можно, не сохраняя локально.
0
0 / 0 / 0
Регистрация: 03.09.2022
Сообщений: 69
05.03.2024, 18:28  [ТС]
xellan24rus, бесспорно, но хочется сделать локальную копию, чтоб постоянно не извлекать данные из сети
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16158 / 11278 / 2891
Регистрация: 21.04.2018
Сообщений: 33,164
Записей в блоге: 2
05.03.2024, 18:29
Цитата Сообщение от Dmitry12329 Посмотреть сообщение
Да, в папке содержатся файлы условные: булочка1.json, булочка2.json, булочкаn.json.
Заменить на SQLite - советую в любом случае.
А вот обязательности применения моей реализации нет.
Можно и дерево использовать, тем более, что вложенности только два уровня.

Если редактирования не предусматривается, то реализация примерно должна быть такой:
C#
1
2
3
4
5
6
7
8
9
10
11
12
    public class ExpanderData
    {
        public int Id { get; set; }
 
        public ReadOnlyCollection<ButtonData> Buttons { get; }
        public string? HeaderExpander { get;}
        public ExpanderData(string? headerExpander, params ButtonData[] buttons )
        {
            HeaderExpander = headerExpander;
            Buttons = Array.AsReadOnly(buttons.ToArray());
        }
    }
0
0 / 0 / 0
Регистрация: 03.09.2022
Сообщений: 69
05.03.2024, 18:32  [ТС]
Элд Хасп, тогда встаёт вопрос неочевидный, стоит ли все булочки хранить в одной таблице или создать множество таблиц
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
05.03.2024, 18:33
Цитата Сообщение от Dmitry12329 Посмотреть сообщение
бесспорно, но хочется сделать локальную копию, чтоб постоянно не извлекать данные из сети
Тут уже на ваше усмотрение. Так как один запрос равен 1 файлу, то я не стал бы ради этого делать бд. Можно сравнивать существование файла и если его нету, то отправлять запрос чтобы получить json. И тем более средства Net как на сервере так везде хорошо работают с json, даже деселиризовать не нужно толком. http Client это умеет сам, на сервере аналогично json парсится в модель сам. Поэтому работа станет намного легче. И вы можете использовать структуры что сделает ваше приложение малость быстрее.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16158 / 11278 / 2891
Регистрация: 21.04.2018
Сообщений: 33,164
Записей в блоге: 2
05.03.2024, 22:35
Лучший ответ Сообщение было отмечено Dmitry12329 как решение

Решение

Dmitry12329, вот такая реализация:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    public class ButtonDataJson
    {
        public string? NameButton { get; set; }
        public string? UrlButton { get; set; }
 
        public int ExpanderDataId { get; set; }
 
        public ButtonDataJson() { }
 
        public ButtonDataJson(string? nameButton, string? urlButton)
        {
            NameButton = nameButton;
            UrlButton = urlButton;
        }
    }
C#
1
2
3
4
5
6
7
8
9
10
    public class ExpanderDataJson
    {
        public ReadOnlyCollection<ButtonDataJson> Buttons { get; }
        public string? HeaderExpander { get; }
        public ExpanderDataJson(string? headerExpander, params ButtonDataJson[] buttons)
        {
            HeaderExpander = headerExpander;
            Buttons = Array.AsReadOnly(buttons.ToArray());
        }
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
    public static class ButtonsCreatorJson
    {
        public static IEnumerable<ButtonDataJson> GetButtons(string fileName)
        {
            // ButtonDataJson[] buttons = JsonSerializer.Deserialize<ButtonDataJson[]>(fileName) ?? Array.Empty<ButtonDataJson>();
 
            // Эмуляция заполнения из файла
            ButtonDataJson[] buttons = Enumerable.Range(1, Random.Shared.Next(1, 4)).Select(i => new ButtonDataJson($"Я кнопка #{i}", @$"\\content  #{i}")).ToArray();
 
            return buttons;
        }
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    public class ExpanderCreatorJson
    {
        /// <summary>Читает все Json из указанной папки и конвертирует их в ExpanderDataJson.</summary>
        /// <param name="folderName">Имя папки. Можно с относительным или абсолютным путём.</param>
        /// <returns>Последовательность ExpanderDataJson.</returns>
        public static IEnumerable<ExpanderDataJson> GetExpanders(string folderName)
        {
            // var files = Directory.GetFiles(folderName, "*.json");
 
            // Эмуляция заполнения из папки
            var files = Enumerable.Range(1, Random.Shared.Next(2, 6)).Select(i => $"Я булочка №{i}").ToArray();
 
 
            ExpanderDataJson[] expanders = new ExpanderDataJson[files.Length];
            for (int i = 0; i < files.Length; i++)
            {
                expanders[i]=new($"Я булочка №{i}", ButtonsCreatorJson.GetButtons(files[i]).ToArray());
            }
            return expanders;
        }
    }
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
    public class ExpandersViewModel
    {
        public IEnumerable<ExpanderDataJson> Expanders { get;}
 
        public ExpandersViewModel() 
        {
            Expanders = ExpanderCreatorJson.GetExpanders("json-folder");
 
            ShowUrlContent = new RelayCommand<string>(text => MessageBox.Show($"Скопировано \"{text}\""));
        }
 
        public RelayCommand<string> ShowUrlContent { get;}
    }
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<Window x:Class="Core2024.CF.Dmitry12329.Topic3157580.ExpandersJsonWindow"
        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:local="clr-namespace:Core2024.CF.Dmitry12329.Topic3157580"
        mc:Ignorable="d"
        Title="ExpandersWindow"  Height="700"
        MinHeight="700" 
        Width="238"
        MinWidth="238"
        DataContext="{DynamicResource vm}">
    <FrameworkElement.Resources>
        <local:ExpandersViewModel x:Key="vm"/>
        <DataTemplate x:Key="someDataTemplate" DataType="{x:Type local:ButtonDataJson}">
            <Button Margin="10" Padding="15 5"
                    Content="{Binding NameButton}"
                    Command="{Binding ShowUrlContent, Source={StaticResource vm}}"
                    CommandParameter="{Binding UrlButton}"/>
        </DataTemplate>
        <DataTemplate x:Key="expandleTemplate"
                      DataType="{x:Type local:ExpanderDataJson}">
            <Expander Header="{Binding HeaderExpander}"
                      Background="Purple">
                <ItemsControl ItemsSource="{Binding Buttons}"
                              ItemTemplate="{StaticResource someDataTemplate}"/>
            </Expander>
        </DataTemplate>
    </FrameworkElement.Resources>
    <UniformGrid >
        <ItemsControl ItemsSource="{Binding Expanders}"
                      ItemTemplate="{DynamicResource expandleTemplate}">
        </ItemsControl>
    </UniformGrid>
</Window>
0
0 / 0 / 0
Регистрация: 03.09.2022
Сообщений: 69
06.03.2024, 00:20  [ТС]
Элд Хасп, всё же возник ещё вопрос. Хочется прикрепить сбоку ScrollViewer, так как при раскрытии содержимого, оно может стать нечитаемым. Но проблема в том, что необходимо использовать статическую компоновку, которая подстраивается под размеры окна. (как я понял, в стиле ScrollViewer содержится DataGrid, у которого размеры явно не указаны и из-за этого возникает проблема).
То есть хочется сделать отступ со всех сторон по 10. Делаю через Margin, но при этом Scroll неактивен (ибо StackPanel подстраивается под размеры содержимого автоматически. Необходимо создавать свой элемент?). Немного плохого кода ниже:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<Window x:Class="Tests.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:local="clr-namespace:Tests"
        mc:Ignorable="d"
        Title="MainWindow" 
        Height="700"
        MinHeight="700"
        Width="238"
        MinWidth="238" 
        x:Name="Window"
        DataContext="{DynamicResource vm}">
    <FrameworkElement.Resources>
        <local:ExpanderCreator x:Key="vm"/>
        <DataTemplate x:Key="someDataTemplate" DataType="{x:Type local:ButtonData}">
            <Button Content="{Binding NameButton}"
                    Background="#A79F9F"
                    Padding="3"
                    Margin="10 5 10 0"
                    Command=""/>
        </DataTemplate>
        <DataTemplate x:Key="expandleTemplate"
                      DataType="{x:Type local:ExpanderData}">
            <Expander Header="{Binding HeaderExpander}"
                      Margin="0 0 0 5"
                      Padding="0 0 0 5"
                      Background="#A49393">
                <ItemsControl ItemsSource="{Binding ButtonsCollection, Source={StaticResource vm}}"
                              ItemTemplate="{StaticResource someDataTemplate}"/>
            </Expander>
        </DataTemplate>
    </FrameworkElement.Resources>
    <StackPanel Margin="5 15 5 15">
        <Grid Background="#8D4747"
              Margin="20 0 20 0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Label Grid.Column="0"
                       Content="User"
                       HorizontalContentAlignment="Right"/>
            <Label Grid.Column="1"
                       Content="Aleksey"
                       HorizontalContentAlignment="Left"/>
        </Grid>
        <StackPanel Margin="10 45 10 15"  >
            <ScrollViewer MaxHeight="200" >
            <ItemsControl
                Name="ContainerExpander"
              ItemsSource="{Binding ExpandersCollection}"
              ItemTemplate="{DynamicResource expandleTemplate}">
            </ItemsControl>
        </ScrollViewer>
        </StackPanel>
 
    </StackPanel>
    
</Window>
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
06.03.2024, 04:24
Dmitry12329, DataGrid находится не в стиле ScrollViewer.
DataGrid расположен как контент ScrollViewer(a) чтобы изменить его, вам нужно изменить компоновку таблицы для ScrollViewer.
Найдите ScrollViewer с именем x:Name="DG_ScrollViewer" по ссылке в этом шаблоне
https://learn.microsoft.com/ru... esktop-4.8
0
0 / 0 / 0
Регистрация: 03.09.2022
Сообщений: 69
06.03.2024, 18:06  [ТС]
Элд Хасп, xellan24rus, всё оказалось куда проще, чем я думал. Проблема была в выборе контейнера, StackPanel подстраивается под содержимое. Но теперь возникла другая проблема, я хотел бы, чтобы ползунок сразу не принимал своё статичное положение (положение 1). А изменялся вместе с Expander, до достижения статичного положения (положение 1)
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<Window x:Class="Tests.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:local="clr-namespace:Tests"
        mc:Ignorable="d"
        Title="MainWindow" 
        Height="700"
        MinHeight="700"
        Width="238"
        MinWidth="238" 
        x:Name="Window"
        DataContext="{DynamicResource vm}">
    <FrameworkElement.Resources>
        <local:ExpanderCreator x:Key="vm"/>
        <DataTemplate x:Key="someDataTemplate" DataType="{x:Type local:ButtonData}">
            <Button Content="{Binding NameButton}"
                    Background="#A79F9F"
                    Padding="3"
                    Margin="10 5 10 0"
                    Command=""/>
        </DataTemplate>
        <DataTemplate x:Key="expandleTemplate"
                      DataType="{x:Type local:ExpanderData}">
            <Expander Header="{Binding HeaderExpander}"
                      Margin="0 0 0 5"
                      Padding="0 0 0 5"
                      Background="#A49393">
                <ItemsControl ItemsSource="{Binding ButtonsCollection, Source={StaticResource vm}}"
                              ItemTemplate="{StaticResource someDataTemplate}"/>
            </Expander>
        </DataTemplate>
    </FrameworkElement.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Name="Hgh" Height="*"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0" Margin="25 10 50 45" Background="#8D4747">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Label Grid.Column="0"
                   Content="User"
                   HorizontalContentAlignment="Right"/>
            <Label Grid.Column="1"
                   Content="Aleksey"
                   HorizontalContentAlignment="Left"/>
        </Grid>
            <ScrollViewer Margin="10 0 10 15" Grid.Row="1">
                <ItemsControl ItemsSource="{Binding ExpandersCollection}"
                              ItemTemplate="{DynamicResource expandleTemplate}" >
                </ItemsControl>
            </ScrollViewer>
        </Grid>
</Window>
0
 Аватар для xellan24rus
364 / 296 / 55
Регистрация: 08.04.2020
Сообщений: 1,175
06.03.2024, 19:17
Dmitry12329, можете нарисовать пример. Не совсем понял что вам нужно.
0
0 / 0 / 0
Регистрация: 03.09.2022
Сообщений: 69
07.03.2024, 00:06  [ТС]
xellan24rus, Элд Хасп, сейчас всё как на картинке 1. Зафиксирован ScrollViewer там, где нужно. Но на начальном этапе я хочу разместить его наравне с содержимым внутри окна. Когда раскрываются expander и постепенно движутся к концу места, ScrollViewer двигается вместе с ним, пока не достигнет финальной точки (либо будет скрыт до этого времени). Позже визуальная составляющая изменится.
Кстати, изменил шаблоны элементов, где их лучше хранить, чтоб не делать сильно громоздким view?
Не знаю, сильно ли хорошо, но для того, чтобы нарисовать линию под Header expander меняю свойство Opacity...
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
        <!--ExpanderStyle-->
        <SolidColorBrush x:Key="Expander.Static.Circle.Stroke" Color="Transparent"/>
        <SolidColorBrush x:Key="Expander.Static.Circle.Fill" Color="Transparent"/>
        <SolidColorBrush x:Key="Expander.Static.Arrow.Stroke" Color="Black"/>
        <SolidColorBrush x:Key="Expander.MouseOver.Circle.Stroke" Color="Transparent"/>
        <SolidColorBrush x:Key="Expander.MouseOver.Circle.Fill" Color="Transparent"/>
        <SolidColorBrush x:Key="Expander.MouseOver.Arrow.Stroke" Color="Black"/>
        <SolidColorBrush x:Key="Expander.Pressed.Circle.Stroke" Color="Transparent"/>
        <SolidColorBrush x:Key="Expander.Pressed.Circle.Fill" Color="Transparent"/>
        <SolidColorBrush x:Key="Expander.Pressed.Arrow.Stroke" Color="Black"/>
        <SolidColorBrush x:Key="Expander.Disabled.Circle.Stroke" Color="Transparent"/>
        <SolidColorBrush x:Key="Expander.Disabled.Circle.Fill" Color="Transparent"/>
        <SolidColorBrush x:Key="Expander.Disabled.Arrow.Stroke" Color="Black"/>
        <Style x:Key="ExpanderHeaderFocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Border>
                            <Rectangle Margin="0" StrokeDashArray="1 2" Stroke="Black" SnapsToDevicePixels="true" StrokeThickness="1"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="ExpanderDownHeaderStyle" TargetType="{x:Type ToggleButton}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                        <Border Padding="{TemplateBinding Padding}">
                            <Grid Background="Transparent" SnapsToDevicePixels="False">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="19"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <Ellipse x:Name="circle" Fill="{StaticResource Expander.Static.Circle.Fill}" HorizontalAlignment="Center" Height="19" Stroke="{StaticResource Expander.Static.Circle.Stroke}" VerticalAlignment="Center" Width="19"/>
                                <Path x:Name="arrow" Data="M 1,1.5 L 4.5,5 L 8,1.5" HorizontalAlignment="Center" Stroke="{StaticResource Expander.Static.Arrow.Stroke}" SnapsToDevicePixels="false" StrokeThickness="2" VerticalAlignment="Center"/>
                                <ContentPresenter Grid.Column="1" HorizontalAlignment="Left" Margin="4,0,0,0" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Center"/>
                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="true">
                                <Setter Property="Data" TargetName="arrow" Value="M 1,4.5  L 4.5,1  L 8,4.5"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Stroke" TargetName="circle" Value="{StaticResource Expander.MouseOver.Circle.Stroke}"/>
                                <Setter Property="Fill" TargetName="circle" Value="{StaticResource Expander.MouseOver.Circle.Fill}"/>
                                <Setter Property="Stroke" TargetName="arrow" Value="{StaticResource Expander.MouseOver.Arrow.Stroke}"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter Property="Stroke" TargetName="circle" Value="{StaticResource Expander.Pressed.Circle.Stroke}"/>
                                <Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
                                <Setter Property="Fill" TargetName="circle" Value="{StaticResource Expander.Pressed.Circle.Fill}"/>
                                <Setter Property="Stroke" TargetName="arrow" Value="{StaticResource Expander.Pressed.Arrow.Stroke}"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Stroke" TargetName="circle" Value="{StaticResource Expander.Disabled.Circle.Stroke}"/>
                                <Setter Property="Fill" TargetName="circle" Value="{StaticResource Expander.Disabled.Circle.Fill}"/>
                                <Setter Property="Stroke" TargetName="arrow" Value="{StaticResource Expander.Disabled.Arrow.Stroke}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="ExpanderUpHeaderStyle" TargetType="{x:Type ToggleButton}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                        <Border Padding="{TemplateBinding Padding}">
                            <Grid Background="Transparent" SnapsToDevicePixels="False">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="19"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <Grid>
                                    <Grid.LayoutTransform>
                                        <TransformGroup>
                                            <TransformGroup.Children>
                                                <TransformCollection>
                                                    <RotateTransform Angle="180"/>
                                                </TransformCollection>
                                            </TransformGroup.Children>
                                        </TransformGroup>
                                    </Grid.LayoutTransform>
                                    <Ellipse x:Name="circle" Fill="{StaticResource Expander.Static.Circle.Fill}" HorizontalAlignment="Center" Height="19" Stroke="{StaticResource Expander.Static.Circle.Stroke}" VerticalAlignment="Center" Width="19"/>
                                    <Path x:Name="arrow" Data="M 1,1.5 L 4.5,5 L 8,1.5" HorizontalAlignment="Center" Stroke="{StaticResource Expander.Static.Arrow.Stroke}" SnapsToDevicePixels="false" StrokeThickness="2" VerticalAlignment="Center"/>
                                </Grid>
                                <ContentPresenter Grid.Column="1" HorizontalAlignment="Left" Margin="4,0,0,0" RecognizesAccessKey="True" SnapsToDevicePixels="True" VerticalAlignment="Center"/>
                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="true">
                                <Setter Property="Data" TargetName="arrow" Value="M 1,4.5  L 4.5,1  L 8,4.5"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Stroke" TargetName="circle" Value="{StaticResource Expander.MouseOver.Circle.Stroke}"/>
                                <Setter Property="Fill" TargetName="circle" Value="{StaticResource Expander.MouseOver.Circle.Fill}"/>
                                <Setter Property="Stroke" TargetName="arrow" Value="{StaticResource Expander.MouseOver.Arrow.Stroke}"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter Property="Stroke" TargetName="circle" Value="{StaticResource Expander.Pressed.Circle.Stroke}"/>
                                <Setter Property="StrokeThickness" TargetName="circle" Value="1.5"/>
                                <Setter Property="Fill" TargetName="circle" Value="{StaticResource Expander.Pressed.Circle.Fill}"/>
                                <Setter Property="Stroke" TargetName="arrow" Value="{StaticResource Expander.Pressed.Arrow.Stroke}"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Stroke" TargetName="circle" Value="{StaticResource Expander.Disabled.Circle.Stroke}"/>
                                <Setter Property="Fill" TargetName="circle" Value="{StaticResource Expander.Disabled.Circle.Fill}"/>
                                <Setter Property="Stroke" TargetName="arrow" Value="{StaticResource Expander.Disabled.Arrow.Stroke}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="ExpanderStyle1" TargetType="{x:Type Expander}">
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="VerticalContentAlignment" Value="Stretch"/>
            <Setter Property="BorderBrush" Value="Transparent"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Expander}">
                        <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="10" SnapsToDevicePixels="true">
                            <DockPanel>
                                <!--Background, Paddin in up-->
                                <ToggleButton x:Name="HeaderSite" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" DockPanel.Dock="Top" FontStretch="{TemplateBinding FontStretch}" Foreground="{TemplateBinding Foreground}" FocusVisualStyle="{StaticResource ExpanderHeaderFocusVisual}" FontStyle="{TemplateBinding FontStyle}" FontFamily="{TemplateBinding FontFamily}" FontWeight="{TemplateBinding FontWeight}" FontSize="{TemplateBinding FontSize}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" Margin="1" MinWidth="0" MinHeight="0" Padding="{TemplateBinding Padding}" Style="{StaticResource ExpanderDownHeaderStyle}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
                                <Border x:Name="Bor" CornerRadius="0 0 10 10" Padding="0 0 0 5" Opacity="0" BorderThickness="0 2 0 0" BorderBrush="Black" Background="#F0ECEC">
                                    <ContentPresenter x:Name="ExpandSite" DockPanel.Dock="Bottom" Focusable="false" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Visibility="Collapsed"/>
                                </Border>
                            </DockPanel>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded" Value="true">
                                <Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
                                <Setter Property="Opacity" TargetName="Bor" Value="100"/>
                            </Trigger>
                            <Trigger Property="ExpandDirection" Value="Right">
                                <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Right"/>
                                <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Left"/>
                                <Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderRightHeaderStyle}"/>
                            </Trigger>
                            <Trigger Property="ExpandDirection" Value="Up">
                                <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Top"/>
                                <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Bottom"/>
                                <Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderUpHeaderStyle}"/>
                            </Trigger>
                            <Trigger Property="ExpandDirection" Value="Left">
                                <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Left"/>
                                <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Right"/>
                                <Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderLeftHeaderStyle}"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                                
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="FocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!--ButtonStyle-->
        <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
        <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
        <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
        <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
        <SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
        <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
        <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
        <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
        <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
        <Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
            <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
            <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
            <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Padding" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border x:Name="border" CornerRadius="10" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                            <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsDefaulted" Value="true">
                                <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
                                <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
 
 
    </FrameworkElement.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0" Margin="25 10 50 45">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Label Style="{DynamicResource LabelUserLeft}"
                   Grid.Column="0"
                   Content="User"
                   HorizontalContentAlignment="Right"/>
            <Label Style="{DynamicResource LabelUserRight}"
                   Grid.Column="1"
                   Content="Aleksey"
                   HorizontalContentAlignment="Left"/>
        </Grid>
        <ScrollViewer Margin="10 0 10 15" Grid.Row="1">
            <ItemsControl ItemsSource="{Binding ExpandersCollection}"
                          ItemTemplate="{DynamicResource expandleTemplate}">
            </ItemsControl>
        </ScrollViewer>
    </Grid>
</Window>
Миниатюры
Вложенные View элементы. Взаимодействие с ViewModel   Вложенные View элементы. Взаимодействие с ViewModel  
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
07.03.2024, 00:06

Вложенные коллекции и viewmodel
Привет всем. Делаю учебную программу для создания тестов. Использую: WPF, EntityFramework, AutoFac, Prism, MVVM. У меня есть...

Binding View к ViewModel
Есть Модель, это компания, с кучей public свойств c OnPropertyChanged(Адрес, телефон, email и т.д.) Есть View, с кучей полей для свойств...

LineGeometry + Model-View-ViewModel
Здравствуйте, выполняю учебное задание и столкнулся со следующей трудностью: у меня есть ...

Определить View по типу ViewModel
собственно такой вопрос, у меня есть вм с сервисом, который что-то делает, в данном случае не имеет значения что: public class...

Взаимодействие нескольких ViewModel с 1-й моделью
Здравствуйте. Разбираюсь сейчас в тонкостях MVVM архитектуры. И возникла необходимость обеспечить взаимодействие нескольких ViewModel с 1-й...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Нейтральные знания ..., ... чистая наука. Пока что-то проходит модерацию на Хабре, стоит развить мысль ...
Hrethgir 20.07.2026
К таким радикальным взглядам я конечно в той публикации не приходил, но чтобы скоротать вечер, решил углубиться немного. 1. Почему показания термометра заряжены целью? Цель заложена в самом. . .
Установка нескольких штампов электронной подписи в строго определенных местах файла docx
ВладимирСамохин 19.07.2026
(В!) Работа с Электронной подписью - это неотъемлемая часть современного документооборота. Но что делать, если нужно поставить несколько штампов электронной подписи в строго определенных местах. . .
сукцессия 35. Научная статья о проделанной работе
anaschu 19.07.2026
Написал в формате латекс и пдф
Вангую, что это не пройдёт модерацию, и на неделе я запущу свой сервер.
Hrethgir 19.07.2026
Эта публикация сейчас в песочнице и ждёт приглашения. https:/ / habr. com/ ru/ sandbox/ 295048/ По ссылке 403. Не очень информативно такую ссылку постить. Запись от Usaga размещена Сегодня в 06:46 . . .
сукцессия 33. открытые вопросы от клауде
anaschu 19.07.2026
"Что накопилось за эту часть А — тринадцать правок, из которых шесть пришли из ваших вопросов и каждая оказалась реальной ошибкой, а не калибровкой: односторонний симбиоз, отсутствующий листопад,. . .
32 сукцессия
anaschu 19.07.2026
сукцессия 28‑мерное ядро стабилизировано Коллеги, фиксирую разбор инженерных правок и их изоморфную проекцию на экономику, меметику и половой отбор. Модель теперь не «подкручивает» сходимость —. . .
сукцессия 31: модель микоризы - это модель ещё нескольких явлений, социальных и экономических
anaschu 18.07.2026
Теория «Всего»: апдейт v1. 1. 2 — 28‑мерное ядро стабилизировано Коллеги, фиксирую разбор инженерных правок и их изоморфную проекцию на экономику, меметику и половой отбор. Модель теперь не. . .
сукцессия 30. Массив проверяющих друг друга моделей
anaschu 18.07.2026
Архитектура сети взаимопроверяющих моделей микоризной сукцессии (v2. 0) Развитие тензорного ОДУ-ядра и создание кросс-платформенного калибровочного полигона Уважаемые коллеги! В продолжение. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru