C# 使用来自目标控件的绑定路径在样式设置器中进行XAML绑定
我有一个客户提出了一个有趣的要求,其中涉及到很多挑战,我认为这是最容易的,结果却是最难的 用户需要知道某个值已在本地更改,但尚未保留到后端存储。(脏状态)我们使用页面上每个控件中声明的样式上的数据触发器解决了这个问题。更改值时,控件背景将填充黄色,然后在按下保存按钮时重置回控件默认值 ModelView实现了一个自定义接口:ILocalValueCache它有一个索引器,该索引器应返回布尔值,以指示自上次数据刷新以来当前值是否已更改C# 使用来自目标控件的绑定路径在样式设置器中进行XAML绑定,c#,wpf,xaml,binding,C#,Wpf,Xaml,Binding,我有一个客户提出了一个有趣的要求,其中涉及到很多挑战,我认为这是最容易的,结果却是最难的 用户需要知道某个值已在本地更改,但尚未保留到后端存储。(脏状态)我们使用页面上每个控件中声明的样式上的数据触发器解决了这个问题。更改值时,控件背景将填充黄色,然后在按下保存按钮时重置回控件默认值 ModelView实现了一个自定义接口:ILocalValueCache它有一个索引器,该索引器应返回布尔值,以指示自上次数据刷新以来当前值是否已更改 ModelView还实现IDataErrorInfo并使用D
- ModelView还实现IDataErrorInfo并使用DataAnnotations属性进行验证,因此我不能简单地使用验证模板
<TextBox Text="{Binding ScaleName}" Margin="5,2,5,2" Grid.Row="1" Grid.Column="2">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding ScaleNameIsLocal}" Value="True">
<Setter Property="Background" Value="{StaticResource LocalValueBackgroundBrush}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBox x:Name="nbScaleCap" Text="{Binding ScaleCap}" Grid.Row="3" Grid.Column="0" Margin="5,2,5,2">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding ScaleCapIsLocal}" Value="True">
<Setter Property="Background" Value="{StaticResource LocalValueBackgroundBrush}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBox x:Name="nbTareTol" Text="{Binding TareTol}" Grid.Row="3" Grid.Column="1" Margin="5,2,5,2">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding TareTolIsLocal}" Value="True">
<Setter Property="Background" Value="{StaticResource LocalValueBackgroundBrush}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
我曾经尝试过在接口上使用索引器来获取IsLocal值,但在INotifyPropertyChanged实现(让模型引发索引器值更改事件)方面遇到了困难,除此之外,更大的问题是如何使用基于目标控件上的内容或文本绑定路径而不是绑定结果值的绑定生成单个样式
我受IDataErrorInfo模式的启发,使用Validation.ErrorTemplate,它表面上看起来很简单,像这样一个简单的重复模式似乎是WPF应该能够处理的,没有太多问题
我不确定多久需要一次这个确切的模板,但我确定我想再次使用这个模式,其中每个属性都有可能有多个状态(不仅仅是错误),并使用状态作为触发器应用不同的样式
我之所以编辑这篇文章,是因为我还没有找到我想要的东西,但多亏了Nikkita,我离这篇文章又近了一步:) 通过使用自定义附加属性,我们可以直接在控件中声明与标志字段的绑定,现在可以在全局样式字典中正确定义样式触发器 ViewModel没有更改,但上面的XML现在已简化:
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<Trigger Property="att:IsLocal.Value" Value="True">
<Setter Property="Background" Value="{StaticResource LocalValueBackgroundBrush}" />
</Trigger>
</Style.Triggers>
</Style>
当前解决方案的最大问题是,如果我想将此(或其他)接口模式应用于现有应用程序,我仍然需要编辑大量现有的XAML。即使在当前的表单中,也有20多个字段,因此有20个可能会导致绑定错误或意外跳过一个字段。我建议您使用“验证器”模式(查看规范INotifyDataErrorInfo)并结合自定义行为。
验证器根据项中绑定的属性名称和Bahaviour对元素的更改,将结果装箱到集合中。查看MSDN帮助
Xaml Example:
<TextBox
Name="a"
Text="{Binding *Variable*, Mode=OneWay}"
Header="Start"
Style ="{StaticResource MitfahrenDefaultTextEdit}" IsReadOnly="true"
Tapped="StartLocation_OnTapped">
<interactivity:Interaction.Behaviors>
<behaviors:RedFormFieldOnErrors
PropertyErrors="{Binding Path=Record.ValidationCollection[*Variable*]}"/>
</interactivity:Interaction.Behaviors>
</TextBox>
Xaml示例:
与其使用这么多标志,不如创建一个附加到文本框的项目,如IsTextLocallyChanged&为所有文本框创建一个样式。如果我理解错误,请纠正我。这不会绕过标志,但应该绕过样式问题,这将简化XAML。然后我可以绑定到视图模型中的标志。问题是视图模型持有该标志,因此我们仍然必须声明XAML。你让我走对了方向。。。我将研究如何在自定义附加属性上进行全局绑定。如果您是对的,我确实希望使用一种与验证器模式非常相似的模式。事实上,这就是这个问题的核心所在。但是我的控件已经有了验证模板,我现在想用额外的模板来扩展它们,比如如果值已经更改并且还没有被持久化,还要进行验证。我可以做界面和模型视图方面的事情,但是因为我想针对现有的控件(这些控件是密封的)我认为我能想到的最接近的东西是某种自定义扩展,它采用了主绑定路径,并在源上的自定义索引器中使用了该路径。通过将模板绑定到错误的属性,我已经犯了错误,我希望它是自动的,如果绑定源没有实现所需的接口,它会自动失败。因此,一些具有属性的后台逻辑和反射引擎可以与旧值进行比较,并为行为提供索引器。行为可以是数据模板中的模板吗?
<TextBox Text="{Binding ScaleName}" Margin="5,2,5,2" Grid.Row="1" Grid.Column="2" att:IsLocal.Value="{Binding ScaleNameIsLocal}"></TextBox>
<TextBox Text="{Binding ScaleCap}" Grid.Row="3" Grid.Column="0" Margin="5,2,5,2" att:IsLocal.Value="{Binding ScaleCapIsLocal}"></TextBox>
<TextBox Text="{Binding TareTol}" Grid.Row="3" Grid.Column="1" Margin="5,2,5,2" att:IsLocal.Value="{Binding TareTolIsLocal}"></TextBox>
Xaml Example:
<TextBox
Name="a"
Text="{Binding *Variable*, Mode=OneWay}"
Header="Start"
Style ="{StaticResource MitfahrenDefaultTextEdit}" IsReadOnly="true"
Tapped="StartLocation_OnTapped">
<interactivity:Interaction.Behaviors>
<behaviors:RedFormFieldOnErrors
PropertyErrors="{Binding Path=Record.ValidationCollection[*Variable*]}"/>
</interactivity:Interaction.Behaviors>
</TextBox>