简体   繁体   English

使用目标控件中的绑定路径在样式设置器中进行XAML绑定

[英]XAML Binding in Style Setter using the Binding Path from the target control

I had an interesting request from a client, there were a number challenges involved, the one that I thought would be the easiest, turned out to be the hardest. 我从一个客户那里提出了一个有趣的要求,其中涉及许多挑战,我认为那是最简单的挑战,却是最困难的挑战。

The user needs to know that a value has been changed locally, but not yet persisted to the backend store. 用户需要知道某个值已在本地更改,但尚未持久保存到后端存储中。 (A dirty state) We solved this with a data trigger on a style declared within each control on the page. (脏状态)我们通过在页面上每个控件内声明的样式的数据触发器解决了此问题。 The control background will be filled with yellow when the value is changed, then reset back to the control default when the save button is pressed. 更改值时,控件背景将以黄色填充,然后按保存按钮将其重置为控件默认值。

The ModelView implements a custom interface : ILocalValueCache This has an indexer that should return Boolean to indicate if the current value has changed since the last data refresh. ModelView实现一个自定义接口: ILocalValueCache它具有一个索引器,该索引器应返回Boolean以指示自上次刷新数据以来当前值是否已更改。

  • The ModelView also Implements IDataErrorInfo and uses DataAnnotations Attributes for validation, so I can't simply use validation templates. ModelView还实现IDataErrorInfo并使用DataAnnotations属性进行验证,因此我不能简单地使用验证模板。

What I would like to do is simplify the XAML using a single Style or a control template this is hard because each control now has two bindings, one to Value and another to Value IsLocal : 我想做的是使用单个样式或控件模板简化XAML,这很难,因为每个控件现在都有两个绑定,一个绑定到Value ,另一个绑定到Value IsLocal

To be more specific, I would prefer a solution where the developer doesn't need to know the inner mechanics of how it works (what the field names for the xIsLocal flags are) or if the binding source even supports it. 更具体地说,我更喜欢这样一种解决方案:开发人员无需了解其工作方式的内部机制(xIsLocal标志的字段名称是什么),也不需要绑定源甚至支持它。

Because the ViewModel implements this interface, (like IDataErrorInfo) I should be able to globally target control styles bound to the states described by the interface. 因为ViewModel实现了此接口(例如IDataErrorInfo),所以我应该能够全局地定位绑定到该接口描述的状态的控件样式。

Here is a section of the XAML with some of the textboxes: 这是XAML的一部分,其中包含一些文本框:

         <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>

As well as the indexer, each property on the View Model has an xxxIsLocal reciprocal property, So the following partial model corresponds to the above example: 除索引器外,视图模型上的每个属性都有一个xxxIsLocal倒数属性,因此以下部分模型与上面的示例相对应:

string ScaleName { get; set; }
bool ScaleNameIsLocal { get; set; }
string ScaleCap { get; set; }
bool ScaleCapIsLocal { get; set; }
string TareTol { get; set; }
bool TarTolIsLocal { get; set; }

I've played around with using the indexer on the interface to get the IsLocal value but struggled with INotifyPropertyChanged implementation (getting the model to raise the indexer value changed event), that aside the bigger issue was how to make a single style with a binding that is based on the path of the content or text binding on the target control instead of the value of the binding result. 我一直在尝试使用接口上的索引器来获取IsLocal值,但在INotifyPropertyChanged实现中苦苦挣扎(让模型引发索引器值更改事件),除了更大的问题外,如何使用绑定来制作单一样式它基于目标控件上内容或文本绑定的路径,而不是绑定结果的值。

I was inspired by the IDataErrorInfo pattern and using the Validation.ErrorTemplate, it looks simple on the surface and such a simple repetitive pattern like this seems like something that WPF should be able to handle without too many issues. 我受到IDataErrorInfo模式和Validation.ErrorTemplate的启发,它看起来很简单,像这样的简单重复模式似乎WPF应该可以处理而不会出现太多问题。

I am not sure how often I will need this exact template, but it's a pattern that I'm sure I'd like to use again, where there is a potential for each property to have multiple states (not just Error) and to apply a different style using the state as a trigger. 我不确定需要多少次使用这个确切的模板,但是我确定我想再次使用这种模式,因为每个属性都有可能具有多个状态(不仅是错误)而且可以应用使用状态作为触发器的另一种样式。


I've edited this post because I haven't quite found what I wanted but thanks to Nikkita I am a step closer :) 我编辑了这篇文章是因为我还没有找到我想要的东西,但是由于有了Nikkita,我才更加靠近了:)

By using a custom attached property, we can declare the binding to the flag field directly in the control and can now properly define the style triggers in a global style dictionary. 通过使用自定义的附加属性,我们可以直接在控件中声明对flag字段的绑定,现在可以在全局样式字典中正确定义样式触发器。

The ViewModel has not changed, but the XML from above is now simplifed: 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>

<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>

My biggest issue with the current solution is that I would still need to edit a lot of existing XAML if I wanted to apply this (or another) interface pattern to existing apps. 当前解决方案的最大问题是,如果我想将此(或其他)界面模式应用于现有应用,则仍需要编辑大量现有XAML。 Even in the current form there are over 20 fields, so that's 20 opportunities to get the binding wrong, or to accidentally skip one. 即使以当前的形式,也有20多个字段,因此这是20个错误的绑定或偶然跳过的机会。

I would suggest you the "validator" pattern (look to spec INotifyDataErrorInfo) combined with custom Behaviour. 我建议您将“验证器”模式(按照INotifyDataErrorInfo的规范进行查看)与自定义行为结合使用。 Validator crates the collection with results according bound property names in item and Bahaviour change the element. 验证程序根据item中绑定的属性名称,用结果创建集合,然后Bahaviour更改元素。 Check the MSDN help. 检查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>

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

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