ContentControls上的WPF DataTriggers在绑定的依赖项属性的PropertyChangedCallback之后触发

时间:2017-09-08 11:09:54

标签: wpf xaml datatrigger contentcontrol cobol.net

我正在编写一个WPF输入对话框窗口,它将根据名为InputType的依赖项属性显示不同的Control。 我使用的语言是Visual COBOL .NET,但问题与语言无关,而与WPF本身无关,VB和C#程序员很容易理解该语言。 这是我的对话窗口的XAML代码

<Window x:Name="wndDialog"
    x:Class="ClassLibraryNew.AGInputBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:lib="clr-namespace:ClassLibraryNew"
    xmlns:ctrl="clr-namespace:ClassLibraryNew.Controls"
    Width="400"
    MinHeight="200"
    WindowStyle="None"
    WindowStartupLocation="CenterOwner"
    ResizeMode="NoResize"
    Background="#FFEEEEEE"
    SizeToContent="Height"
    MouseDown="OnMouseDown"
    Loaded="OnLoaded">
<Window.CommandBindings>
    <CommandBinding Command="{x:Static lib:DialogCommands.OkCommand}" CanExecute="OnCommandCanExecute" Executed="OnCommandExecuted"/>
    <CommandBinding Command="{x:Static lib:DialogCommands.CancelCommand}" CanExecute="OnCommandCanExecute" Executed="OnCommandExecuted"/>
</Window.CommandBindings>
<Border Style="{StaticResource AGTWindowBorder}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <GroupBox Grid.Row="0" Header="{Binding Caption}">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <TextBlock Grid.Row="0"
                           Text="{Binding Text}"
                           FontSize="{Binding Converter={StaticResource FontSizeConverter}, ConverterParameter='16'}"
                           VerticalAlignment="Center"
                           TextWrapping="Wrap"/>

                <ContentControl Grid.Row="1" x:Name="contentControl" >
                    <ContentControl.Style>
                        <Style TargetType="{x:Type ContentControl}">
                            <Setter Property="ContentTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <TextBlock Foreground="Red" Text="Errore: input type non valido. Contattare l'assistenza tecnica."/>
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding InputType}" Value="Text">
                                    <Setter Property="ContentTemplate">
                                        <Setter.Value>
                                            <DataTemplate>
                                                <ctrl:TextField Text="{Binding Value, ElementName=wndDialog}"
                                                                ctrl:WatermarkService.Watermark="{Binding WatermarkText, ElementName=wndDialog}"
                                                                ctrl:WatermarkService.HideWhenFocused="False"
                                                                MaxLength="{Binding MaxLength, ElementName=wndDialog}"/>
                                            </DataTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding InputType}" Value="Integer">
                                    <Setter Property="ContentTemplate">
                                        <Setter.Value>
                                            <DataTemplate>
                                                <ctrl:IntegerField Value="{Binding Value, ElementName=wndDialog}"
                                                                   ZeroFill="{Binding ZeroFill, ElementName=wndDialog}"
                                                                   MaxLength="{Binding MaxLength, ElementName=wndDialog}"/>
                                            </DataTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding InputType}" Value="Decimal">
                                    <Setter Property="ContentTemplate">
                                        <Setter.Value>
                                            <DataTemplate>
                                                <ctrl:DecimalField Value="{Binding Value, ElementName=wndDialog}"
                                                                   DecimalDigits="{Binding DecimalDigits, ElementName=wndDialog}"/>
                                            </DataTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ContentControl.Style>
                </ContentControl>
            </Grid>
        </GroupBox>
        <StackPanel Grid.Row="1" 
                    Orientation="Horizontal" 
                    HorizontalAlignment="Right"
                    Margin="8">
            <StackPanel.Resources>
                <Style TargetType="{x:Type Button}" BasedOn="{StaticResource EuroButton}">
                    <Setter Property="Width" Value="80"/>
                    <Setter Property="Margin" Value="8,0,0,0"/>
                </Style>
            </StackPanel.Resources>
            <Button IsDefault="True"
                    Command="{x:Static lib:DialogCommands.OkCommand}">
                <AccessText Text="_Ok"/>
            </Button>
            <Button IsCancel="True"
                    Command="{x:Static lib:DialogCommands.CancelCommand}">
                <AccessText Text="_Annulla"/>
            </Button>
        </StackPanel>
    </Grid>
</Border>

我想要实现的是当InputType为Text时向用户显示'TextField'(自定义TextBox),当InputType为Integer时显示'IntegerField'等。 InputType的类型是枚举名称DialogInputType,它包含三个值(Text,Integer,Decimal)。 这工作正常,但我需要一种方法来将事件处理程序附加到ContentControl内的Field,当其内容已正确设置且不为null。 我期望DataTriggers在InputType更改时重新评估,而不是失败: (Visual COBOL .NET)

   01  InputTypeProperty type DependencyProperty public static initialize only
       value type DependencyProperty::Register(
           "InputType",
           type of DialogInputType,
           type of AGInputBox,
           new FrameworkPropertyMetadata(
               type DialogInputType::Text,
               new PropertyChangedCallback(method OnInputTypeChanged)
           )
       ).
   *> property definition omitted...

   method-id OnInputTypeChanged private static.
   procedure division using by value sender as type DependencyObject, by value e as type DependencyPropertyChangedEventArgs.
       if sender not instance of type AGInputBox
           goback
       end-if
       declare wnd as type AGInputBox = sender as type AGInputBox
       if wnd::contentControl::Content instance of type FieldBase *> debugger arrives here
           declare textField as type FieldBase
           set textField = wnd::contentControl::Content as type FieldBase
           attach method wnd::OnFieldTemplateApplied to textField::TemplateApplied
       end-if
   end method.

VS调试器显示ContentControl的内容为空,但是窗口正确可视化,并且可能稍后设置其内容... 在以下情况下它也是空的: - 窗口的加载事件 - 窗口的ContentRendered事件 我无法在DataTemplate控件中设置Loaded RoutedEventHandler,也不能使用Loaded =“OnFieldLoaded”或Style + EventSetter,因为它被禁止并且不会编译(即使编译器错误建议使用EventSetter:/)。

编辑:我尝试了l33t的解决方案但遗憾的是OnContentChanges永远不会被调用,即使内容设置正确也是如此。 我创建了这个类:

   class-id ClassLibraryNew.Controls.NotifyingContentControl public
       inherits type ContentControl.

   01  ContentChanged type EventHandler event public.

   method-id new public.
   procedure division.
       invoke super::new()
   end method.

   method-id OnContentChanged protected override.
   procedure division using by value oldContent as object, by value newContent as object.
       invoke super::OnContentChanged(oldContent, newContent) *> I put a debugger breakpoint here but it's not getting hit
       invoke RaiseContentChanged()
   end method.

   method-id RaiseContentChanged private.
   procedure division.
       declare handler as type EventHandler = ContentChanged
       declare e as type EventArgs = new EventArgs()
       if handler not = null
           invoke run handler(by value self, e)
       end-if
   end method.

   end class.

2 个答案:

答案 0 :(得分:1)

即使您设法确定内容的价值,也无法保证这是您在用户界面中看到的价值。

你可以试试这个:

public class ContentControlEx : ContentControl
{
    protected override void OnContentChanged(object oldContent, object newContent)
    {
        // Do stuff...
        base.OnContentChanged(oldContent, newContent);
    }
}

然后使用ContentControlEx代替常规的。{/ p>

答案 1 :(得分:1)

Loaded定义为资源并处理TextFieldIntegerFieldDecimalField根元素的<ContentControl Grid.Row="1" x:Name="contentControl" > <ContentControl.Resources> <DataTemplate x:Key="tfTemplate"> <ctrl:TextField ... Loaded="LoadedHandler"/> </DataTemplate> <!-- + DataTemplates for IntegerField and DecimalField --> </ContentControl.Resources> <ContentControl.Style> <Style TargetType="{x:Type ContentControl}"> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <TextBlock Foreground="Red" Text="Errore: input type non valido. Contattare l'assistenza tecnica."/> </DataTemplate> </Setter.Value> </Setter> <Style.Triggers> <DataTrigger Binding="{Binding InputType}" Value="Text"> <Setter Property="ContentTemplate" Value="{StaticResource tfTemplate}" /> </DataTrigger> <DataTrigger Binding="{Binding InputType}" Value="Integer"> <Setter Property="ContentTemplate" Value="{StaticResource ifTemplate}" /> </DataTrigger> ... </Style.Triggers> </Style> </ContentControl.Style> </ContentControl> 事件:

v-for