Гость
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Привязка от UserControl к ViewModel / 12 сообщений из 12, страница 1 из 1
11.07.2019, 12:59
    #39836319
Eld Hasp
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
Столкнулся с непонятным (для меня) поведением привязки

Простая ViewModel
Код: c#
1.
2.
3.
4.
5.
6.
7.
    public class ViewModel : OnPropertyChangedClass
    {
        private int _value = 10;

        public int Value { get => _value; set { _value = value; OnPropertyChanged(nameof(Value)); } }

    }



Простой UC с одним DP-свойством
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
<UserControl x:Name="userControl"
             x:Class="CyberForum.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="180" d:DesignWidth="160">
    <Grid>
        <TextBlock  Text="{Binding Value, ElementName=userControl}"/>
    </Grid>
</UserControl>

Код: 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.
    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }

        public int MinValue { get; } = 5;


        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register
            (
                nameof(Value), 
                typeof(int),
                typeof(UserControl1),
                new FrameworkPropertyMetadata(0, ValuePropertyChanged,ValuePropertyValidate)
            );

        public int Value
        {
            get { return (int)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        private static void ValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            /// Для установки точки останова
            /// при отладке
        }

        private static object ValuePropertyValidate(DependencyObject d, object e)
        {
            UserControl1 x = (UserControl1)d;
            int newValue = (int)e;

            if (newValue < x.MinValue)
                return x.MinValue;

            return newValue;
        }
    }



View использующая указанные VM и UC
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="UserContol     " Margin="10"/>
            <local:UserControl1 x:Name="uc" Margin="10" Value="{Binding Value, Mode=TwoWay}"/>
        </StackPanel>
        <TextBlock Margin="10">
            <Run Text="Значение из ViewModel:"/>
            <Run Text="{Binding Value}"/>
        </TextBlock>
        <TextBlock Margin="10">
            <Run Text="Значение из UserControl:"/>
            <Run Text="{Binding Value, ElementName=uc}"/>
        </TextBlock>

        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Новое значение" Margin="10"/>
            <TextBox Width="50" x:Name="tb" Margin="10"/>
        <Button Content="Записать в UserControl новое значение" Click="Bt_Click" Margin="10"/>
        </StackPanel>
    </StackPanel>



В UC идёт ограничение присваиваемого Value значения по минимальному значению 5.
Если присвоить значение меньше пяти, то в UC и ViewModel оказываются разные значения.
В UC установленное минимальное значение, в ViewModel - то значение которое пытались установить в UC.

Вопрос - так и должно быть или я где-то туплю?
...
Рейтинг: 0 / 0
11.07.2019, 14:46
    #39836398
Eld Hasp
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
Забыл CB приложить
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

        }

        private void Bt_Click(object sender, RoutedEventArgs e)
        {
            if (int.TryParse(tb.Text, out int value))
                uc.Value = value;
        }
    }
...
Рейтинг: 0 / 0
11.07.2019, 17:09
    #39836490
Сон Веры Павловны
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
Eld HaspВопрос - так и должно быть или я где-то туплю?
Не то что бы так должно быть (по-хорошему), но оно действительно так: CoerceValueCallback удерживает только значение DependencyProperty, на значения, передаваемые через биндинг, он не влияет. Обычно в таком случае, если удерживаемое значение не равно переданному, дополнительно делают что-то типа
Код: c#
1.
GetBindingExpression(ValueProperty)?.UpdateSource();
...
Рейтинг: 0 / 0
11.07.2019, 18:21
    #39836533
Eld Hasp
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
Сон Веры ПавловныEld HaspВопрос - так и должно быть или я где-то туплю?
Не то что бы так должно быть (по-хорошему), но оно действительно так: CoerceValueCallback удерживает только значение DependencyProperty, на значения, передаваемые через биндинг, он не влияет. Обычно в таком случае, если удерживаемое значение не равно переданному, дополнительно делают что-то типа
Код: c#
1.
GetBindingExpression(ValueProperty)?.UpdateSource();


Это пробовал. Куда не вставь UpdateSource() тоже всё криво работает.
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
        private static void ValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            /// Для установки точки останова
            /// при отладке
            BindingOperations.GetBindingExpression(d, ValueProperty)?.UpdateSource();
        }

        private static object ValuePropertyValidate(DependencyObject d, object e)
        {
            UserControl1 x = (UserControl1)d;
            int newValue = (int)e;

            if (newValue < x.MinValue)
            {
                 BindingOperations.GetBindingExpression(d, ValueProperty)?.UpdateSource();
                return x.MinValue;
            }
            BindingOperations.GetBindingExpression(d, ValueProperty)?.UpdateSource();

            return newValue;
        }



Помогает только вариант со вторичным присваиванием значения.
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
        private static object ValuePropertyValidate(DependencyObject d, object e)
        {
            UserControl1 x = (UserControl1)d;
            int newValue = (int)e;

            if (newValue < x.MinValue)
            {
                x.Value = x.MinValue;
                return x.MinValue;
            }

            return newValue;
        }


Но такой код мне кажется "плохо попахивает". Или он норм?
...
Рейтинг: 0 / 0
12.07.2019, 11:32
    #39836735
Сон Веры Павловны
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
Eld HaspНо такой код мне кажется "плохо попахивает". Или он норм?
Других работающих вариантов всё равно нет. Проблема старая, и на неё натыкались многие - вот, например .
...
Рейтинг: 0 / 0
12.07.2019, 11:49
    #39836751
Roman Mejtes
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
может потому, что это не валидация? :) CoerceValueCallback служит для создания ограничений допустимых значений свойства.
Грубо говоря. если у вас есть 3 свойства Min, Max, Value, то логично, что ограничение Max не может быть меньше Min, а Value должно лежать в заданных рамках. В обратную сторону это не действует, ограничения действуют только в рамках самого свойства зависимости.
Если в модели представления задать Max меньше чем Min, в ProgressBar, то значение Max будет просто равно значению Min.
Как это работает можно посмотреть на реализации этого элемента управления.
То есть, это нужно для создания собственных элементов управления, безотносительно модели представления. Чтоб значения свойств не выходили за допустимые рамки.
...
Рейтинг: 0 / 0
12.07.2019, 11:52
    #39836754
Roman Mejtes
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
еще для полного понимания стоит почитать про приоритеты значений, чтоб не возникало вопросов, почему я задаю одно значение свойству, а у него совсем другое значение

https://docs.microsoft.com/ru-ru/dotnet/framework/wpf/advanced/dependency-property-value-precedence
...
Рейтинг: 0 / 0
12.07.2019, 21:00
    #39837009
Eld Hasp
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
Roman Mejtesможет потому, что это не валидация? :) CoerceValueCallback служит для создания ограничений допустимых значений свойства.
Грубо говоря. если у вас есть 3 свойства Min, Max, Value, то логично, что ограничение Max не может быть меньше Min, а Value должно лежать в заданных рамках. В обратную сторону это не действует, ограничения действуют только в рамках самого свойства зависимости.
Если в модели представления задать Max меньше чем Min, в ProgressBar, то значение Max будет просто равно значению Min.
Как это работает можно посмотреть на реализации этого элемента управления.
То есть, это нужно для создания собственных элементов управления, безотносительно модели представления. Чтоб значения свойств не выходили за допустимые рамки.
Так я и пытаюсь организовать ограничение минимального значения.
И делаю именно по примеру от microsoft.
Оно всё вроде работает как и надо. Пока я не задал привязку в свойстве. Кроме этой привязки всё работает.
...
Рейтинг: 0 / 0
12.07.2019, 21:11
    #39837010
Eld Hasp
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
Roman Mejtesеще для полного понимания стоит почитать про приоритеты значений, чтоб не возникало вопросов, почему я задаю одно значение свойству, а у него совсем другое значение

https://docs.microsoft.com/ru-ru/dotnet/framework/wpf/advanced/dependency-property-value-precedence
Прочитал, только не понял какое отношение это имеет к обсуждаемой проблеме.

На мой взгляд это очевидный баг.
В настоящее присвоение значения работает так:
SetValue -> Исходящая привязка в свойстве -> coerceValueCallback -> propertyChangedCallback -> Присвоение значения -> Внешние привязки К свойству

На мой взгляд " Исходящая привязка в свойстве" должна работать после "Присвоения значения" одновременно с "Внешними привязка"
...
Рейтинг: 0 / 0
12.07.2019, 21:23
    #39837011
Eld Hasp
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
Сон Веры ПавловныEld HaspНо такой код мне кажется "плохо попахивает". Или он норм?
Других работающих вариантов всё равно нет. Проблема старая, и на неё натыкались многие - вот, например .
Спасибо за внимание к теме!
Придётся пользоваться костылём.
Странно, что от MS нет никакой реакции на столь давно обсуждаемый баг (на мой взгляд).
...
Рейтинг: 0 / 0
13.07.2019, 13:42
    #39837078
Eld Hasp
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
Roman Mejtesможет потому, что это не валидация? :) CoerceValueCallback служит для создания ограничений допустимых значений свойства.
Грубо говоря. если у вас есть 3 свойства Min, Max, Value, то логично, что ограничение Max не может быть меньше Min, а Value должно лежать в заданных рамках. В обратную сторону это не действует, ограничения действуют только в рамках самого свойства зависимости.С задержкой, но понял вашу мысль.
Раз это внутреннее ограничение DP, то и влиять на источник данных она не должна.
...
Рейтинг: 0 / 0
13.07.2019, 13:55
    #39837081
Eld Hasp
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Привязка от UserControl к ViewModel
Нашёл разъяснение от MS
Advanced Coercion and Callback Scenarios
Constraints and Desired Values
The CoerceValueCallback callbacks will be used by the property system to coerce a value in accordance to the logic you declare, but a coerced value of a locally set property will still retain a "desired value" internally. If the constraints are based on other property values that may change dynamically during the application lifetime, the coercion constraints are changed dynamically also, and the constrained property can change its value to get as close to the desired value as possible given the new constraints. The value will become the desired value if all constraints are lifted. You can potentially introduce some fairly complicated dependency scenarios if you have multiple properties that are dependent on one another in a circular manner. For instance, in the Min/Max/Current scenario, you could choose to have Minimum and Maximum be user settable. If so, you might need to coerce that Maximum is always greater than Minimum and vice versa. But if that coercion is active, and Maximum coerces to Minimum, it leaves Current in an unsettable state, because it is dependent on both and is constrained to the range between the values, which is zero. Then, if Maximum or Minimum are adjusted, Current will seem to "follow" one of the values, because the desired value of Current is still stored and is attempting to reach the desired value as the constraints are loosened.

There is nothing technically wrong with complex dependencies, but they can be a slight performance detriment if they require large numbers of reevaluations, and can also be confusing to users if they affect the UI directly. Be careful with property changed and coerce value callbacks and make sure that the coercion being attempted can be treated as unambiguously as possible, and does not "overconstrain".

Using CoerceValue to Cancel Value Changes
The property system will treat any CoerceValueCallback that returns the value UnsetValue as a special case. This special case means that the property change that resulted in the CoerceValueCallback being called should be rejected by the property system, and that the property system should instead report whatever previous value the property had. This mechanism can be useful to check that changes to a property that were initiated asynchronously are still valid for the current object state, and suppress the changes if not. Another possible scenario is that you can selectively suppress a value depending on which component of property value determination is responsible for the value being reported. To do this, you can use the DependencyProperty passed in the callback and the property identifier as input for GetValueSource, and then process the ValueSource.
...
Рейтинг: 0 / 0
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Привязка от UserControl к ViewModel / 12 сообщений из 12, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]