Я довольно новичок в WPF и, возможно, мне не хватает чего-то простого, но я попробовал несколько простых примеров для привязки пользовательских элементов управления, и они работают, но когда я пытаюсь применить их в нашей ситуации, привязка работает только тогда, когда я изменяю значение в пользовательском элементе управления, но если я изменяю его из другого места, пользовательский элемент управления не отражает это изменение.
Мы используем Prism, и у нас естьViewModel и View для отображения и редактирования свойств устройства. Эти свойства читаются из XML при запуске и добавляются в устройство как словарь настроек. И Настройки, и Устройство реализуют INotifyPropertyChanged, и когда значение Настройки изменяется, Устройство также вызывает событие Изменено свойство для этого Настройки.
Таким образом, при запуске программы значения отображаются следующим образом: красная стрелка - это новый пользовательский элемент управления, а синяя стрелка указывает рабочий код непосредственно в исходное состояние представления:
Если я изменю значение в пользовательском элементе управления, оно будет обновлено и в другом. Привязка к источнику в порядке:
Но если я изменю его на другой, он не будет обновлен в пользовательском элементе управления. Привязка из источника не в порядке:
Также, если я изменяю значение условия str_pressureUnit, преобразование выполняется только в старом коде. Привязка условия преобразования не в порядке:
Но элемент управления «Включить / отключить» работает правильно для обоих. Включите ok:
Простые примеры с другим пользовательским элементом управления и свойствами из устройства, которые не являются динамическими, работают нормально, если я использую `Mode =" TwoWay». Я думал, что, возможно, это проблема с Multibindings или с конфигурацией Dynamic Settings, но поскольку работает с EnableProperties (что является обычным свойством ViewModel), я подозреваю, что это может быть что-то, связанное со свойствами Dynamic.
Вот так выглядит наш класс Device:
public class Device : DynamicObject, INotifyPropertyChanged
{
#region Properties
...
public Dictionary<string, ISetting> DynamicSettings { get; private set; } = new Dictionary<string, ISetting>();
...
public Device(SettingDefinitionsProvider settingDefinitionsProvider, ICommunicationChannel communicationChannel, DeviceType deviceType)
{
...
foreach(ISetting s in DynamicSettings.Values)
{
s.PropertyChanged += OnSettingValueUpdated;
}
}
...
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName]string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
#endregion //INotifyPropertyChanged
#region EventHandlers
private void OnSettingValueUpdated(object sender, PropertyChangedEventArgs e)
{
if(sender is ISetting)
{
OnPropertyChanged((sender as ISetting).Name);
}
}
#endregion //EventHandlers
}
И класс Setting генерирует событие PropertyChanged при изменении его значения.
ViewModel имеет свойство SelectedDevice ион установлен как DataContext представления с функциональностью AutoWireViewModel Prism.
Мы также используем MultiBinding и некоторые сложные конвертеры, потому что некоторые отображаемые значения параметров могут изменяться в зависимости от другого значения параметра, а также от включенного элемента управления и т. д. Таким образом, одна запись в XAML для параметра может получить такой большой размер:
<TextBlock Grid.Row="4" Text="{Binding SelectedDevice.SafetyMargin.DisplayName}" Margin="5"/>
<TextBox Grid.Row="4" Grid.Column="1" Margin="5">
<TextBox.IsEnabled>
<MultiBinding Converter="{conv:EnableControlConverter}">
<Binding RelativeSource="{RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}" Path="DataContext.SelectedDevice.SafetyMargin"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}" Path="DataContext.EnableControls"/>
</MultiBinding>
</TextBox.IsEnabled>
<MultiBinding Converter="{conv:ConversionConverter}">
<Binding RelativeSource="{RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}" Path="DataContext.SelectedDevice.SafetyMargin"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}" Path="DataContext.SelectedDevice.PressureUnit"/>
</MultiBinding>
</TextBox>
<TextBlock Grid.Row="4" Grid.Column="2" Margin="5">
<TextBlock.Text>
<MultiBinding Converter="{conv:UnitLabelConverter}">
<Binding RelativeSource="{RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}" Path="DataContext.SelectedDevice.SafetyMargin"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type UserControl}, Mode=FindAncestor}" Path="DataContext.SelectedDevice.PressureUnit"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Чтобы упростить добавление новых параметров (это является причиной чтения из xml и т. Д.), Я создал новыйПользовательский контроль. Его XAML:
<UserControl ...
x:Name="parent">
<Grid DataContext="{Binding ElementName=parent}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Path=Setting.DisplayName}" Margin="5"/>
<TextBox Grid.Column="1" Margin="5">
<TextBox.IsEnabled>
<MultiBinding Converter="{conv:EnableControlConverter}">
<Binding Path="Setting"/>
<Binding Path="Enabled"/>
</MultiBinding>
</TextBox.IsEnabled>
<MultiBinding Mode="TwoWay" Converter="{conv:ConversionConverter}">
<Binding Path="Setting"/>
<Binding Path="ConditionSetting"/>
</MultiBinding>
</TextBox>
<TextBlock Grid.Column="2" Margin="5">
<TextBlock.Text>
<MultiBinding Converter="{conv:UnitLabelConverter}">
<Binding Path="Setting"/>
<Binding Path="ConditionSetting"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
И код позади:
public partial class CustomTextBox : UserControl
{
protected static Logger logger = LogManager.GetCurrentClassLogger();
public static readonly DependencyProperty SettingProperty =
DependencyProperty.Register("Setting", typeof(object),
typeof(CustomTextBox), new PropertyMetadata(""));
public static readonly DependencyProperty ConditionSettingProperty =
DependencyProperty.Register("ConditionSetting", typeof(object),
typeof(CustomTextBox), new PropertyMetadata(""));
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.Register("Enabled", typeof(object),
typeof(CustomTextBox), new PropertyMetadata(""));
public object Setting
{
get { return (object)GetValue(SettingProperty); }
set { SetValue(SettingProperty, value); }
}
public object ConditionSetting
{
get { return (object)GetValue(ConditionSettingProperty); }
set { SetValue(ConditionSettingProperty, value); }
}
public object Enabled
{
get { return (object)GetValue(EnabledProperty); }
set { SetValue(EnabledProperty, value); }
}
public CustomTextBox()
{
InitializeComponent();
}
}
И это новый код в представлении:
<controls:CustomTextBox Grid.Row="3"
Setting="{Binding SelectedDevice.SafetyMargin, Mode=TwoWay}"
ConditionSetting="{Binding SelectedDevice.PressureUnit, Mode=TwoWay}"
Enabled="{Binding EnableControls}"/>
Любой совет будет высоко оценен, заранее спасибо.