Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/29: Рейтинг темы: голосов - 29, средняя оценка - 5.00
13 / 12 / 4
Регистрация: 20.05.2016
Сообщений: 325
1

[WPF] Привязка свойств контролов к значениям локальных переменных

16.12.2016, 20:00. Показов 5255. Ответов 15
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Имеется List, в котором, допустим, по индексу 0 содержится булевое значение, определяющее должен ли быть checkbox ischecked (true) или наоборот.
Мне нужно в xaml разметке сделать привязку к этому листу и указать обращение к первому элементу.
То что я имею ввиду: IsChecked="{Binding local:App.AnyList[0]}".

Поскольку с binding'ом к локальным переменным не знаком, задаю банальный вопрос: это двусторонняя связь? При изменении ischecked меняется ли значение, к которому оно привязано?

В гугле ответа не нашёл конкретно про листы.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
16.12.2016, 20:00
Ответы с готовыми решениями:

Рассмотреть программу, написать имена глобальных переменных, локальных переменных, формальных параметров
Program P1; var s:string; procedure P(var s:string) ; var i, j : integer; ...

Создание свойств у контролов
Создал свой контрол на основе Button. Как можно у своего контрола создавать свойства, которые...

Изменение свойств вложенных контролов
Здравствуйте, появился такой вопрос. Есть у меня компонент, на нём, допустим, лежит ComboBox, и он...

Перебор контролов с изменением свойств
Перебираю с помощью цикла все контролы ToolBar на форме: For Each ctr In RunForm.Controls If...

15
57 / 30 / 13
Регистрация: 24.06.2014
Сообщений: 255
Записей в блоге: 1
16.12.2016, 23:43 2
dm stark, почему элементы должны храниться в List ?
Можно вот так схитрить:
C#
1
2
3
4
5
6
7
8
9
10
11
private List<object> myList = new List<object>();
 
public bool IsCheckboxCheced
        {
            get { return (bool)this.myList[0]; }
            set 
            { 
                this.myList[0]= value;
                OnPropertyChanged("Data_type1_");
            }
        }
И привязать это свойство к чекбоксу.
0
13 / 12 / 4
Регистрация: 20.05.2016
Сообщений: 325
17.12.2016, 08:57  [ТС] 3
Да нет. Не совсем в привязке дело. Мне нужно это для хранения настроек. Чтобы можно было из List KeyValuePairs доставать по Key (string) конеретные значения (bool в данном случае). Это не только для checkbox'ов. Пока с ними надо разобраться)

List использую, потому что Dictionary не сериализуем XmlSerializer. Как-то же я должен настройки сохранить...

Да и связь должна быть двусторонней: меняешь checkbox - меняется значение.
0
57 / 30 / 13
Регистрация: 24.06.2014
Сообщений: 255
Записей в блоге: 1
17.12.2016, 13:44 4
Цитата Сообщение от dm stark Посмотреть сообщение
Да и связь должна быть двусторонней: меняешь checkbox - меняется значение.
Если указать тип привязки TwoWay получишь двустороннею привязку. У чекбокса она по умолчанию двусторонняя.

Цитата Сообщение от dm stark Посмотреть сообщение
List использую, потому что Dictionary не сериализуем XmlSerializer. Как-то же я должен настройки сохранить...
Обязательно нужно в xml сериализовать? Есть еще бинарный формат.

Кроме того можно написать свой механизм для чтения/записи в xml. Читать тут.
Я бы примерно так и поступил:
1. Создал view model для настроек.
2. Создал свойство для привязки каждой настройки.
3. При старте приложения загружал сохраненные настройки.
4. При изменении настроек переписывал данные в файле.

MVVM подход тем и хорош, что ты берешь данные (model) делаешь для них обертку, удобную для работы (view model) и отображаешь данные как нужно в view.

Если List используется ради самого List, то это не самое лучшее решение. Тем более, что для его использования нужно городить костыли.
0
13 / 12 / 4
Регистрация: 20.05.2016
Сообщений: 325
17.12.2016, 14:21  [ТС] 5
Да. Бинарная сериализация получше будет)

Но зачем для каждой настройки свойство делать? Я видел как то можно через свой класс наследуемый от Binding. Там в Source просто Dictionary даётся с настройками. А далее используеься этот класс в привязке. Как это можно сделать? Как такой класс написать? Он не большой.
0
57 / 30 / 13
Регистрация: 24.06.2014
Сообщений: 255
Записей в блоге: 1
17.12.2016, 16:07 6
Если у тебя для каждой настройки будет отдельный UI элемент, то наиболее логично сделать отдельное свойство и не искать тяжелых путей.

Если все настройки будут в одном элементе типа ListView (я использую GridControl от devexpress, но он платный).
Тогда тебе нужна ViewModel отвечающая за отдельную настройку (например с свойствами Name, Value и т.д.).
Эти VM будут храниться в ObservableCollection, которую нужно привязать к свойству ItemSource.

Если нужны разные типы контролов для настроек то можно использовать ItemTemplateSelector.

Таким образом ты получишь коллекцию с настройками, но ни List ни Dictionary не являются кандидатами на эту роль.
0
13 / 12 / 4
Регистрация: 20.05.2016
Сообщений: 325
17.12.2016, 18:41  [ТС] 7
Не. У меня настройки для всего приложения. Там несколько страниц. Никакого одного ListView нет. Всё в Grid по обычному)

Вот статья, но что-то не получается. Объясните, пожалуйста, в чём проблема: http://www.broculos.net/2014/0... FP2YhuLSM_

Добавлено через 3 минуты
А где про свойства: у меня есть класс ApplicationSettings, где в Dictionary хранятся все настройки; я должен просто сделать свойства в каком-нибудь статическом классе, где извлекаю из Dictionary нужные значения в get property и изменяю в аксессоре set. Так я понимаю? Но у меня там свойств под сотню будет...

Добавлено через 30 минут
Думаю более правильно найти тяжёлый, но требующий много кода способ, чем описывать всё свойства. Многовато выйдет.

Добавлено через 1 час 23 минуты
*не требующий много кода способ
0
57 / 30 / 13
Регистрация: 24.06.2014
Сообщений: 255
Записей в блоге: 1
17.12.2016, 19:04 8
Цитата Сообщение от dm stark Посмотреть сообщение
Думаю более правильно найти тяжёлый, но требующий много кода способ, чем описывать всё свойства. Многовато выйдет.
Можно использовать генерацию кода. В VS есть такая штука t4.
Я с его помощью генерирую базовый набросок view model с model (тип и имя свойства модели извлекаю через рефлексию).

Цитата Сообщение от dm stark Посмотреть сообщение
Вот статья, но что-то не получается. Объясните, пожалуйста, в чём проблема: http://www.broculos.net/2014/0... FP2YhuLSM_
На сколько я понял, он использует некую абстракцию над механизмом настоек WPF проекта. Его класс позволяет вводить настройки не в дизайнере, а в коде.

C#
1
2
3
4
5
private void Initialize()
    {
        this.Source = Settings.Default.Config;
        this.Mode = BindingMode.TwoWay;
    }
Тут он устанавливает, что все привязки будут обращаться к классу Settings.Default.Config. Этот класс статический.
Правда мне не понятно как обновляются привязки при изменении источника (настройки), так как класс не реализует INotifyPropertyChanged.

Цитата Сообщение от dm stark Посмотреть сообщение
Никакого одного ListView нет. Всё в Grid по обычному)
Но grid то один?

Я предлагал следующее решение:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SettingViewModel : INotifyPropertyChanged
{
   // простая реализация view model
   public string Name {//...}
   public string Value {//...}
}
 
public class AppSettingsViewModel : INotifyPropertyChanged
{
    //....
    public ObservableCollection<SettingViewModel> ItemSource { get {//....}}
    
    private void LoadAppSettings()
    {
         // читать с файла в коллекцию
         // создать SettingViewModel для каждого элемента коллекции
         // и добавить в ItemSource 
     }
}
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
<ListView Name="lstw" Margin="5" ItemSource="{Binding ItemSource}">
          <ListView.DataContext>
                  <local: AppSettingsViewModel>
          </ListView.DataContext>
          <ListView.View>
                    <GridView>
                        <GridView.Columns>
                            <GridViewColumn Header="Имя" DisplayMemberBinding="{Binding Path=Name}"></GridViewColumn>
                            <GridViewColumn Header="Значение" DisplayMemberBinding="{Binding Path=Value}"></GridViewColumn>
                        </GridView.Columns>
                    </GridView>
          </ListView.View>
     </ListView>
Чтобы иметь в приложении один экземпляр AppSettingsViewModel можно создать его как синглтон или использовать ServiceLocator, в котором зарегистрировать его инстанс как синглтон.

Я использую вот такой подход.
1
13 / 12 / 4
Регистрация: 20.05.2016
Сообщений: 325
17.12.2016, 21:36  [ТС] 9
Лучше синглтон. В моём классе настроек это уже реализовано. Сейчас переделаю как вы написали.

Добавлено через 21 минуту
Спасибо

Добавлено через 1 час 9 минут
Поясните пожалуйста следующее:
я вот заменю ItemSource в своём случае на Settings. да и зачем мне реализовывать INotifyPropertyChanged? чтобы при изменении сразу в файл вносить данные. Но вы представляете когда куча настроек, какая это будет нагрузка? Каждый раз всю коллекцию сериализовать бинарно, зашифровать, записать. я просто ещё шифрую настройки. мне не нужна реализация этого интерфейса. я лучше в background потоке буду каждые 5 минут это делать и при событии app.exit.

мне менять collection на dictionary? а для checkbox'ов тогда как вот это сделать? установить в datacontext="local:classname" ischecked="{Binding *как то извлечь из dictionary keyvalue по строковому key*}"???

Вот и вернулись к исходному вопросу.

Добавлено через 38 минут
ладно. просто можете написать свой пример как было только для checkbox. без какого-либо listview.
вот вся разметка для вас:
XML
1
2
3
<Grid>
     <CheckBox IsChecked="{...}" ... />
</Grid>
0
57 / 30 / 13
Регистрация: 24.06.2014
Сообщений: 255
Записей в блоге: 1
17.12.2016, 22:16 10
Цитата Сообщение от dm stark Посмотреть сообщение
INotifyPropertyChanged
нужен для того, что бы при изменении источника т.е. значения настройки, все элементы которые его используют подтянули новое значение. При изменении одной настройки не обязательно обновлять файл с настройками. Это вообще 2 разные задачи.
Настройки можно сохранять при закрытии view с настройками или через определенное время в фоне.

Цитата Сообщение от dm stark Посмотреть сообщение
написать свой пример как было только для checkbox
Не понял вопроса.
0
13 / 12 / 4
Регистрация: 20.05.2016
Сообщений: 325
17.12.2016, 22:17  [ТС] 11
А.. Понял с propchenged.

Ну. свой пример использования класса AppSettingsViewModel для задания свойства ischecked checkbox'а. без всяких listview.
0
57 / 30 / 13
Регистрация: 24.06.2014
Сообщений: 255
Записей в блоге: 1
17.12.2016, 22:42 12
Ага, т.е. что бы без коллекции настроек. Тогда был бы класс с свойством для каждой настройки. DataContext устанавливал бы элементу верхнего уровня, который содержит grid с контролами. Делал бы это с застраничного кода, так как бы ресолвил его через ServiceLocator. По идее если AppSettingsViewModel будет синглтоном, то можна оставить вариант с установкой DataContext в XAML.
0
13 / 12 / 4
Регистрация: 20.05.2016
Сообщений: 325
17.12.2016, 22:59  [ТС] 13
Выходит, невозможно связать свойство контрола со значением dictionary?

Добавлено через 24 секунды
Ну как то же это делают по ссылке я вам дал.

Добавлено через 14 минут
а.. ну там вообщем тоже свойства.
мда. жаль что со свойствами тыкаться приходится))
0
57 / 30 / 13
Регистрация: 24.06.2014
Сообщений: 255
Записей в блоге: 1
18.12.2016, 01:25 14
Видимо такая модель была выбрана при создании WPF. Если мы хотим что то использовать как источник данных, то нужен способ отслеживать изменения этих данных. Свойство просто оказалось удобной абстракцией.
0
40 / 32 / 7
Регистрация: 24.03.2016
Сообщений: 270
25.12.2016, 04:45 15
Цитата Сообщение от dm stark Посмотреть сообщение
Выходит, невозможно связать свойство контрола со значением dictionary?
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
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
public partial class ObservableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
    {
        public ObservableDictionary() : base() { }
        public ObservableDictionary(int capacity) : base(capacity) { }
        public ObservableDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { }
        public ObservableDictionary(IDictionary<TKey, TValue> dictionary) : base(dictionary) { }
        public ObservableDictionary(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer) { }
        public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer) { }
 
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        public event PropertyChangedEventHandler PropertyChanged;
 
        public new TValue this[TKey key]
        {
            get
            {
                return base[key];
            }
            set
            {
                TValue oldValue;
                bool exist = base.TryGetValue(key, out oldValue);
                var oldItem = new KeyValuePair<TKey, TValue>(key, oldValue);
                base[key] = value;
                var newItem = new KeyValuePair<TKey, TValue>(key, value);
                if (exist)
                {
                    this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, base.Keys.ToList().IndexOf(key)));
                }
                else {
                    this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, base.Keys.ToList().IndexOf(key)));
                    this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
                }
            }
        }
 
        public new void Add(TKey key, TValue value)
        {
            if (!base.ContainsKey(key))
            {
                var item = new KeyValuePair<TKey, TValue>(key, value);
                base.Add(key, value);
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, base.Keys.ToList().IndexOf(key)));
                this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
            }
        }
 
        public new bool Remove(TKey key)
        {
            TValue value;
            if (base.TryGetValue(key, out value))
            {
                var item = new KeyValuePair<TKey, TValue>(key, base[key]);
                bool result = base.Remove(key);
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, base.Keys.ToList().IndexOf(key)));
                this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
                return result;
            }
            return false;
        }
 
        public new void Clear()
        {
            base.Clear();
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        }
 
        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, e);
            }
        }
 
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, e);
            }
        }
    }

Я пролистал вниз и увидел только что-то про "нельзя бинд делать на словарь", вот вам, используйте, и привязывайтесь

Добавлено через 1 минуту
C#
1
2
3
4
5
public ObservableDictionary<string, int> Bosses
        {
            get { return _bosses; }
            set { _bosses = value; OnPropertyChanged("Bosses"); }
        }
Как оно выглядит в качестве свойства лично у меня

Добавлено через 38 секунд
XML
1
2
3
4
5
6
7
<ComboBox Height="25"
                                                          Margin="10,0,10,0"
                                                          DisplayMemberPath="Key"
                                                          ItemsSource="{Binding Bosses}"
                                                          Opacity="0.6"
                                                          SelectedValue="{Binding SelectedBoss}"
                                                          SelectedValuePath="Value" />
Как я его привязываю в UI

Добавлено через 51 секунду
А тот самый SelectedBoss, это Интовое свойство, что реализует так же OnPropertyChanged

Добавлено через 1 минуту
dm stark, а вообще у меня настройки сохраняются просто в текстовый файл, а не бинарно сериализуются, что б я смог их изменить вне приложения, как конфиг, знаете?) И там тоже есть значения булевые для тоглбаттонов

Добавлено через 58 секунд
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
StreamWriter sw = new StreamWriter(new FileStream("config.txt", FileMode.Create, FileAccess.Write));
                string configData = $"UseBossFight={UseBossFight}\n"
                    + $"UseMapFight={UseMapFight}\n"
                    + $"UseArenaFight={UseArenaFight}\n"
                    + $"SelectedBoss={SelectedBoss}\n"
                    + $"SelectedMap={SelectedMap}\n"
                    + $"UseDustCraft={UseDustCraft}\n"
                    + $"LandNomber={LandNomber}\n"
                    + $"ClanFightItemsCount={ClanFightItemsCount}\n"
                    + $"IsLeader={IsLeader}\n"
                    + $"ClanFightTimeCooldown={ClanFightTimeCooldown}\n"
                    + $"MaximumBossPower={MaximumBossPower}\n"
                    + $"KatoEnergyValue={KatoEnergyValue}\n"
                    + $"MariEnergyValue={MariEnergyValue}\n"
                    + $"KseifurEnergyValue={KseifurEnergyValue}\n"
                    + $"UseKatoFight={UseKatoFight}\n"
                    + $"UseMariFight={UseMariFight}\n"
                    + $"UseKseifurFight={UseKseifurFight}\n"
                    + $"UseClanFight={UseClanFight}";
                sw.Write(configData);
                sw.Close();
Опять же как пример )

Добавлено через 53 секунды
Затем чтение при запуске :

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
if (File.Exists("./config.txt"))
            {
                temp = File.ReadAllLines("./config.txt");
                foreach (var item in temp)
                {
                    var tempStr = item.Split('=');
                    cfg.Add(tempStr[0], tempStr[1]);
                }
                
                // Применяем настройки
                UseBossFight = Convert.ToBoolean(cfg["UseBossFight"]);
                UseMapFight = Convert.ToBoolean(cfg["UseMapFight"]);
                UseArenaFight = Convert.ToBoolean(cfg["UseArenaFight"]);
                SelectedBoss = Convert.ToInt32(cfg["SelectedBoss"]);
                UseDustCraft = Convert.ToBoolean(cfg["UseDustCraft"]);
                ClanFightItemsCount = Convert.ToInt32(cfg["ClanFightItemsCount"]);
                IsLeader = Convert.ToBoolean(cfg["IsLeader"]);
                ClanFightTimeCooldown = cfg["ClanFightTimeCooldown"];
                MaximumBossPower = cfg["MaximumBossPower"];
                UseClanFight = Convert.ToBoolean(cfg["UseClanFight"]);
                UseKatoFight = Convert.ToBoolean(cfg["UseKatoFight"]);
                UseMariFight = Convert.ToBoolean(cfg["UseMariFight"]);
                UseKseifurFight = Convert.ToBoolean(cfg["UseKseifurFight"]);
                KatoEnergyValue = Convert.ToInt32(cfg["KatoEnergyValue"]);
                MariEnergyValue = Convert.ToInt32(cfg["MariEnergyValue"]);
                KseifurEnergyValue = Convert.ToInt32(cfg["KseifurEnergyValue"]);
            }
2
13 / 12 / 4
Регистрация: 20.05.2016
Сообщений: 325
25.12.2016, 13:54  [ТС] 16
Сами писали ObservableDictionary?)

Чтение настроек мне не подходит. Они шифруются. Это не портативная программа, а приложение для широкого пользования (будет как бы =). Поэтому использую бинарную сериализацию.

А так, я уже решил данную проблему. У меня класс ApplicationSettings со статическим свойством Settings.
Есть свои методы Add (по сути, как Add так и Set), Remove, Get из Settings. В конструкторе дешифрую и десериализую, а так же запускаю background поток для сохранения настроек по таймеру и при app.exit. Мне нет резона каждый раз ловить уведомление о том, что коллекция была изменена. Это какая нагрузка выйдет. Каждый раз шифровать, сериализовать и перезаписывать... А настроек у меня в приложений не мало.

Так использую в разметке:
XML
1
Property="{Binding Source={x:Static local:ApplicationSettings.Settings}, Path=[StringKey], Mode=TwoWay}"
Словарь:
C#
1
public static Dictionary<string, object> Settings { get; set; } = new Dictionary<string, object>();
0
25.12.2016, 13:54
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
25.12.2016, 13:54
Помогаю со студенческими работами здесь

Привязка контролов к настройкам
Здравствуйте, возникла проблема при привязке контролов в настройкам приложения. Суть такая - есть...

Привязка контролов к окну
Форумчане ни как не могу догнать при уменьщении окма мои лэйблы и текстбоксы ни куда не двигаются а...

Изменение свойств нескольких контролов триггером
Доброго времени суток! На форме есть элемент TextBlock и TextBox. TextBox - скрыт. Нужно при...

Изменение свойств контролов по массиву имен
Допустим у меня есть 1024 кнопки и у всех их visible=false и имена у них вписаны от балды, но есть...


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

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru