我目前正在构建一个uwp,用户可以在GUI中添加自定义xaml元素并设置某些属性。还可以设置页面的常规属性。为此,在设计页面上有一个属性面板。在这个属性面板中,我想要显示所选xaml元素的可变属性,或者(如果用户选择一个特定元素)它应该显示常规属性。我想使用双向数据绑定,以便在属性面板中动态显示这些值,同时也将属性面板中所做的更改传递给源。
实现这种双向绑定以显示所选xaml-element的细节没有任何问题,但是对于一般属性,绑定似乎只是单向的。当代码中的常规属性发生更改时,会将其传递给目标对象,但是当在GUI中更改数据时,它不会将数据传递给源。
所以我搜索了网页,尝试通过添加依赖项属性,更改私有< - > public,以各种方式实现INotifyPropertyChanged等来修改我的代码。不幸的是,它似乎都没有工作。
下面我将提供工作和非功能c#code / xaml。如果有人能发现问题我会非常感激。
设计页面中目标控件的常规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>
使用xaml-element
属性的双向绑定数据模板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>
用于设置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;
}
具有可显示属性的元素是从Grid Xaml-control继承的GUIelements。我显示的属性标有GUIElementProperty属性。 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; } }
}
}
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>
具有常规属性的类的代码
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));
}
}
}
创建常规属性类的实例
public sealed partial class DesignPage : Page
{
private SessionProps _props = new SessionProps() {DesignName = "", LimitedTime = false, Timelimit = TimeSpan.Zero};
对此来源的代码绑定控制
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;
}
我希望有一些我可以俯瞰的小东西,但任何帮助都将不胜感激!我希望我提供了足够的信息,但如果还有什么不清楚的地方,请询问。
答案 0 :(得分:0)
原因是LimitedTime
为bool
类型,Timelimit
为TimeSpan类型,TextBox
中的文字为string
类型。编辑TextBox
时,会出现错误:转换器无法将“Windows.Foundation.String”类型的值转换为“布尔”类型;
您可以创建一个类,通过继承 IValueConverter ,您可以在源和目标之间转换数据格式。
您应始终使用功能实现来实现Convert,但实现ConvertBack相当常见,以便报告未实现的异常。如果您使用转换器进行双向绑定,或者使用XAML进行序列化,则只需要在转换器中使用 ConvertBack 方法。
有关详细信息,请参阅INotifyPropertyChanged
。
例如:
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 "";
}
}
}
创建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 "";
}
}
}
在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>