简体   繁体   English

双向绑定仅适用于一项,而不适用于另一项

[英]two-way binding works for one item, but not other

I am currently building an uwp where users can add custom xaml-elements to the GUI and set certain properties. 我目前正在构建一个uwp,用户可以在其中将自定义xaml元素添加到GUI并设置某些属性。 There are also general properties of the page that can be set. 还可以设置页面的常规属性。 To do this there is a properties panel on the designpage.In this properties panel I want to either display the changeable properties from the xaml-element that is selected or (if the user selects one specific element) it should display the general properties. 为此,在设计页面上有一个属性面板。在此属性面板中,我要显示所选xaml元素中的可更改属性,或者(如果用户选择一个特定元素)则应显示常规属性。 I want to use two-way databinding so that the values are dynamically displayed in the property panel, but also so that changes made in the propertypanel are passed to the source. 我想使用双向数据绑定,以便将值动态显示在属性面板中,而且还可以将在属性面板中进行的更改传递给源。

I had no problems implementing this two-way binding for displaying the details of the selected xaml-element, but for the general properties the binding appears to go only one-way. 对于显示所选xaml-element的详细信息,实现该双向绑定没有问题,但是对于常规属性,绑定似乎仅是单向的。 When a general property is changed in code, it is passed to the target object, but when data is changed in the GUI it doesn't pass the data to the source. 在代码中更改常规属性后,会将其传递给目标对象,但是在GUI中更改数据时,则不会将数据传递给源。

So I have searched the web and tried modifying my code by adding dependency properties, changing private<-->public, implementing INotifyPropertyChanged in various ways, etc. Unfortunately none of it seems to be working. 因此,我已经在网上搜索并尝试通过添加依赖项属性,更改private <-> public,以各种方式实现INotifyPropertyChanged等来修改我的代码。不幸的是,这些方法似乎都不起作用。

Below I will provide the both the working and non-functional c# code/xaml. 下面,我将提供有效和无效的c#代码/ xaml。 If anybody can spot the problem I'd be most grateful. 如果有人能发现问题,我将不胜感激。

General xaml for the target control in design page 设计页面中目标控件的常规xaml

<StackPanel x:Name="PropsPanel" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Orientation="Vertical" Background="GhostWhite" BorderThickness="1" BorderBrush="GhostWhite" Margin="4">
         <TextBlock Text="Properties" Margin="4,4,0,40" Foreground="Black" FontWeight="Bold"/>
         <ContentControl Name="PropertiesPanel" Foreground="Black">
         </ContentControl>
</StackPanel>

Datatemplate xaml with working two-way binding for properties xaml-element 具有属性xaml-element的双向绑定的Datatemplate xaml

<DataTemplate x:Key="TimerElementTemplate">
    <Grid HorizontalAlignment="Stretch">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Margin="2" Text="Timer" FontWeight="SemiBold"/>
        <TextBlock Grid.Row="1" Grid.Column="0" Margin="2" Text="Column "/>
        <TextBlock Grid.Row="1" Grid.Column="1" Margin="2" Text="{Binding Path=ShowColumn, Mode=OneWay}" />
        <TextBlock Grid.Row="2" Grid.Column="0" Margin="2" Text="Row " />
        <TextBlock Grid.Row="2" Grid.Column="1" Margin="2" Text="{Binding Path=ShowRow, Mode=OneWay}"/>
        <TextBlock Grid.Row="3" Grid.Column="0" Margin="2" Text="Name "/>
        <TextBox Grid.Row="3" Grid.Column="1" BorderThickness="1" Margin="2" Text="{Binding Path=ElementName, Mode=TwoWay}" />
        <TextBlock Grid.Row="4" Grid.Column="0" Margin="2" Text="Label " />
        <TextBox Grid.Row="4" Grid.Column="1" Margin="2" BorderThickness="1" Text="{Binding Path=Label, Mode=TwoWay}" />
    </Grid>
</DataTemplate>

Working code for setting the datacontext 设置数据上下文的工作代码

private void GOnPointerReleased(object sender, PointerRoutedEventArgs pointerRoutedEventArgs)
    {
        if (_selectedGuiElement != null)
        {
            _selectedGuiElement.BorderThickness = new Thickness(0);
        }
        PropertiesPanel.DataContext = sender;
        PropertiesPanel.ContentTemplate = Resources[sender.GetType().Name + "Template"] as DataTemplate;
        var element = sender as GuiElement;
        element.BorderBrush = new SolidColorBrush(Colors.Crimson);
        element.BorderThickness = new Thickness(2.0);
        _selectedGuiElement = element;

    }

The elements with the displayable properties are GUIelements that inherit from the Grid Xaml-control. 具有可显示属性的元素是从网格Xaml控件继承的GUIelements。 The properties I display are marked with the GUIElementProperty-attribute. 我显示的属性用GUIElementProperty属性标记。 The code for the GUI-element is below: GUI元素的代码如下:

public class GuiElement : Grid
{
    protected string _elementType;

    public GuiElement(bool design)
    {
        DesignState = design;
        SetElementType();
        AddContent();
        if(DesignState)
        AllowDrop = true;
    }

    //overridable method to set Type and Type related properties
    public virtual void SetElementType()
    {
        _elementType = "";
        GuiBackground = new SolidColorBrush(Colors.DarkGray);
    }
    //overridable method to set lay-out content for each type
    public virtual void AddContent()
    {
        Background = GuiBackground;
        BorderThickness = new Thickness(1);
        BorderBrush = new SolidColorBrush(Colors.GhostWhite);
        SetColumnSpan(this, ColumnSpan);
        SetRowSpan(this, RowSpan);
    }


    // shortcuts for Grid.Column and Grid.Row properties and other general properties
    [GuiElementProperty("lbl", "Column", 2)]
    public int Column
    {
        get { return GetColumn(this); }
        set { SetColumn(this, value); }
    }

    public string ShowColumn
    {
        get { return Column.ToString(); }
    }

    [GuiElementProperty("lbl", "Row", 1)]
    public int Row
    {
        get { return GetRow(this); }
        set { SetRow(this, value); }
    }

    public string ShowRow
    {
        get { return Row.ToString(); }
    }

    public int ColumnSpan { get; set; } = 1;

    public int RowSpan { get; set; } = 1;

    public bool DesignState { get; set; }

    public SolidColorBrush GuiBackground { get; set; }

    public int Id { get; set; }

    [GuiElementProperty("lbl", "Type", 0)]
    public string ElementType { get { return _elementType; } }
}

} }

So far the working code...now the problematic part 到目前为止,工作代码...现在是有问题的部分

Datatemplate xaml for displaying the general properties with dysfunctional two-way binding DataTemplate XAML用于显示功能异常的双向绑定的常规属性

<DataTemplate x:Key="StartElementTemplate">
    <Grid HorizontalAlignment="Stretch">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Margin="2" Text="Session" FontWeight="SemiBold"/>
        <!--<Button Foreground="GhostWhite" Grid.Row="0" Grid.Column="1" Content="X" Click="ButtonDeleteFromProperties_OnClick"></Button>-->
        <TextBlock Grid.Row="1" Grid.Column="0" Margin="2" Text="Name "/>
        <TextBlock Grid.Row="1" Grid.Column="1" Margin="2" Text="{Binding Path=DesignName, Mode=OneWay}"/>
        <TextBlock Grid.Row="2" Grid.Column="0" Margin="2" Text="Time Limit (true/false) "/>
        <TextBox Grid.Row="2" Grid.Column="1" Margin="2" BorderThickness="1" Text="{Binding Path=LimitedTime, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Grid.Row="3" Grid.Column="0" Margin="2" Text="Max. duration (hh:mm:ss) "/>
        <TextBox Grid.Row="3" Grid.Column="1" BorderThickness="1" Margin="2" Text="{Binding Path=Timelimit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
</DataTemplate>

the code for the class with the general properties 具有常规属性的类的代码

 public class SessionProps : INotifyPropertyChanged   
{   private string _designname;
   private bool _limitedtime;
   private TimeSpan _timelimit;

   [GuiElementProperty("txtbx", "Design name", 0)]
    public string DesignName
    {
        get { return _designname; }
        set
        {
            _designname = value;
            NotifyPropertyChanged();
        }
    }

    [GuiElementProperty("bool", "Time limit", 1)]
   public bool LimitedTime
    {
        get { return _limitedtime; }
        set
        {
            _limitedtime = value;
            NotifyPropertyChanged();
        }
    }

    [GuiElementProperty("txtbx", "Max. Duration (hh:mm:ss)", 2)]
   public TimeSpan Timelimit
    {
       get { return _timelimit; }
       set
       {
           _timelimit = value;
            NotifyPropertyChanged();
        } 
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Creating an instance of the general properties class 创建常规属性类的实例

 public sealed partial class DesignPage : Page
{
    private SessionProps _props = new SessionProps() {DesignName = "", LimitedTime = false, Timelimit = TimeSpan.Zero};

code binding control to this source 代码绑定控制到此源

 private void SOnPointerReleased(object sender, PointerRoutedEventArgs e)
    {
        if (_selectedGuiElement != null)
        {
            _selectedGuiElement.BorderThickness = new Thickness(0);
        }
        PropsPanel.DataContext = _props;
        PropertiesPanel.ContentTemplate = Resources[sender.GetType().Name + "Template"] as DataTemplate;
        var element = sender as GuiElement;
        element.BorderBrush = new SolidColorBrush(Colors.Crimson);
        element.BorderThickness = new Thickness(2.0);
        _selectedGuiElement = element;
    }

I'm hoping there is just something small I'm overlooking here, but any help will be appreciated! 我希望这里可以忽略的地方很小,但是任何帮助,我们将不胜感激! I hope I have provided sufficient information, but if there is anything that's still unclear, please ask. 希望我提供了足够的信息,但是如果仍然不清楚,请询问。

The reason is that the LimitedTime is bool type, Timelimit is TimeSpan type and the text in TextBox is string type. 原因是LimitedTimebool类型, Timelimit是TimeSpan类型,而TextBox中的TextBoxstring类型。 When you edit the TextBox , it will get an Error: Converter failed to convert value of type 'Windows.Foundation.String' to type 'Boolean'; 当您编辑TextBox ,将出现错误:转换器无法将类型为“ Windows.Foundation.String”的值转换为类型为“ Boolean”的代码;

You can create a class that allows you to convert the format of your data between the source and the target by inheriting from IValueConverter . 您可以创建一个类,该类允许您通过继承IValueConverter在源和目标之间转换数据格式。

You should always implement Convert with a functional implementation, but it's fairly common to implement ConvertBack so that it reports a not-implemented exception. 您应该始终通过函数实现来实现Convert ,但是实现ConvertBack以便报告未实现的异常是相当普遍的。 You only need a ConvertBack method in your converter if you are using the converter for two-way bindings, or using XAML for serialization. 如果您使用转换器进行双向绑定,或使用XAML进行序列化,则仅在转换器中需要ConvertBack方法。

For more info, see INotifyPropertyChanged . 有关更多信息,请参见INotifyPropertyChanged

For example: 例如:

public class BoolFormatter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return value.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        if (value.ToString() == "true" || value.ToString() == "True")
        {
            return true;
        }
        else if (value.ToString() == "false" || value.ToString() == "False")
        {
            return false;
        }
        else
        {
            return "";
        }
    }
}

Create TimeSpanFormatter class: 创建TimeSpanFormatter类:

public class TimeSpanFormatter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value != null)
        {
            return value.ToString();
        }
        else
        {
            return null;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        TimeSpan span;

        if (TimeSpan.TryParse(value.ToString(), out span))
        {
            return span;
        }
        else
        {
            return "";
        }
    }
}

In XAML: 在XAML中:

<Page.Resources>
    <local:BoolFormatter x:Key="BoolConverter" />
    <local:TimeSpanFormatter x:Key="TimeSpanConverter" />
    <DataTemplate>
        <Grid HorizontalAlignment="Stretch">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0" Margin="2" Text="Session" FontWeight="SemiBold" />
            <!--<Button Foreground="GhostWhite" Grid.Row="0" Grid.Column="1" Content="X" Click="ButtonDeleteFromProperties_OnClick"></Button>-->
            <TextBlock Grid.Row="1" Grid.Column="0" Margin="2" Text="Name " />
            <TextBlock Grid.Row="1" Grid.Column="1" Margin="2" Text="{Binding Path=DesignName, Mode=TwoWay}" />
            <TextBlock Grid.Row="2" Grid.Column="0" Margin="2" Text="Time Limit (true/false) " />
            <TextBox Grid.Row="2" Grid.Column="1" Margin="2" BorderThickness="1" Text="{Binding Path=LimitedTime, Mode=TwoWay,Converter={StaticResource BoolConverter}}" />
            <TextBlock Grid.Row="3" Grid.Column="0" Margin="2" Text="Max. duration (hh:mm:ss) " />
            <TextBox Grid.Row="3" Grid.Column="1" BorderThickness="1" Margin="2" Text="{Binding Path=Timelimit, Mode=TwoWay,Converter={StaticResource TimeSpanConverter}}" />
        </Grid>
    </DataTemplate>
</Page.Resources>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM