如何使用样式/数据触发器在数据网格单元中指定控件

时间:2019-05-30 19:18:07

标签: wpf xaml triggers contentcontrol

在WPF中,我试图创建一个自定义数据网格,当存在数据绑定数据列表(数据范围)时,将单元格显示为组合框;如果没有限制,则显示普通文本框。此外,我正在使用IDataErrorInfo和MVVM模式的随附验证。大部分效果很好,但细节在于魔鬼。我看到一个问题,我的组合框上的验证无法按我预期的那样工作。

我相信我的问题是使用数据触发器定义两个控件时,但是我不确定为什么它的行为方式如此。如果删除文本框上的验证,则组合框上的验证可以正常工作。当我将属性放回文本框中以指定验证时,组合框验证将不再起作用。

这是我的xaml代码,用于根据我的数据触发器指定要使用的控件:

<UserControl x:Class="GlobalVariableEditor.GlobalVariableEditorUserControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:viewmodel="clr-namespace:GlobalVariableEditor.ViewModel" 
        xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
        xmlns:local="clr-namespace:GlobalVariableEditor"
        xmlns:clr="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        d:DesignHeight="400" d:DesignWidth="500" Width="auto" Height="auto">

    <UserControl.DataContext>
        <viewmodel:GlobalVariableEditorViewModel x:Name="_GVEViewModel"/>
    </UserControl.DataContext>

    <UserControl.Resources>
        <CollectionViewSource x:Key="VariablesViewSource" Source="{Binding VariablesData}">
        </CollectionViewSource>

        <Style x:Key="RangeStyleNormal" TargetType="ComboBox">            
            <Setter Property="HorizontalContentAlignment" Value="center"/>       
            <!--<EventSetter Event="SelectionChanged" Handler="ComboBox_SelectionChanged"></EventSetter>-->
            <Setter Property="IsEditable" Value="False" />
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="IsTextSearchCaseSensitive" Value="True"/>
        </Style>      

    </UserControl.Resources>
    <GroupBox x:Name="grpGVEditor" Header="Global variable editor" FontSize="12">
        <Grid x:Name="MainGrid">           
            <Grid.RowDefinitions>
                <!--This is for any declarations or headers we may need for initial information (where to load xml file from, etc)-->
                <RowDefinition x:Name="TopRowDefinition" Height="*" MinHeight="30" MaxHeight="50" />
                <RowDefinition x:Name="RevisionRowDefinition" Height="*" MinHeight="30" MaxHeight="50" />
                <!--this is where the data from the xml file should be populated to-->
                <RowDefinition x:Name="DataRowDefinition" Height="*" MinHeight="200"/>
                <!--this is where the buttons should be placed (cancel, apply, etc)-->
                <!--<RowDefinition x:Name="FilterRowDefinition" Height="*" MinHeight ="25" MaxHeight="30"/>-->
                <RowDefinition x:Name="ButtonRowDefinition" Height="*" MinHeight ="30" MaxHeight="50" />
            </Grid.RowDefinitions>

            <StackPanel Grid.Row="0" Orientation="Horizontal">

                <Label Height="30" Width="110" Content="XML file location: " BorderThickness="1" BorderBrush="Black" Margin="3,0,3,0"/>
                <Label x:Name="lblCustomXMLFilePath" VerticalContentAlignment="Center" Height="30" Width="350" Content="{Binding FilePath}" BorderThickness="1" BorderBrush="Black" Margin="0,0,3,0" ToolTip="{Binding FilePath}" Background="Gray"/>
                <!--<Button x:Name="btnLoad" Height="30" Width="50" Content="Load" Margin="0,5,5,5" Click="btnLoad_Click" Visibility="Hidden"/>-->
                <Button Height="30" Width="50" Content="Revert" Margin="0,0,3,0" ToolTip="Revert to TP values" Visibility="Hidden"/>

            </StackPanel>

            <StackPanel Grid.Row="1" Orientation="Horizontal">

                <Label Name="lblRevID" Height="30" Width="110" Content="File Revision ID: " BorderThickness="1" BorderBrush="Black" Margin="3,0,3,0"/>
                <TextBox Name="txtRevID" Padding="3" Height="30" Width="100" VerticalContentAlignment="Center" TextAlignment="Left" BorderThickness="1" BorderBrush="Black" Margin="0,0,3,0" Text="{Binding FileVersion}" IsReadOnly="True" Background="Gray">
                    <TextBox.Style>
                        <Style>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding FileVersion}" Value="-1">
                                    <Setter Property="TextBox.Background" Value="Salmon" />
                                    <Setter Property="TextBox.ToolTip" Value="Invalid revision number.  Settings file is in read-only mode." />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding FileVersion}" Value="0">
                                    <Setter Property="TextBox.Background" Value="Salmon" />
                                    <Setter Property="TextBox.ToolTip" Value="Invalid revision number.  Settings file is in read-only mode." />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </TextBox.Style>
                </TextBox>
                <TextBox x:Name="tbDummy" Visibility="Hidden"></TextBox>
            </StackPanel>
            <DockPanel Grid.Row="2" HorizontalAlignment="Stretch" MinHeight="200" Margin="0,5,0,0">
                <DataGrid x:Name="PropertyGrid" SelectionMode="Single" SelectionUnit="Cell"  RowHeaderWidth="0" IsTextSearchCaseSensitive="True" GridLinesVisibility="All" 
                      AutoGenerateColumns="False" AlternationCount="2" ItemsSource="{Binding FilteredGridEntries}" Margin="3,0,0,0">

                    <DataGrid.RowStyle>
                        <Style TargetType="DataGridRow">
                            <Style.Resources>
                                <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Transparent"/>
                            </Style.Resources>
                            <Setter Property="IsEnabled" Value="{Binding HasWriteAccess}" ></Setter>

                            <Style.Triggers>
                                <Trigger Property="AlternationIndex" Value="0">
                                    <Setter Property="Background" Value="White" />
                                </Trigger>
                                <Trigger Property="AlternationIndex" Value="1">
                                    <Setter Property="Background" Value="WhiteSmoke" />
                                </Trigger>

                                <MultiDataTrigger >
                                    <MultiDataTrigger.Conditions>
                                        <Condition Binding="{Binding IsReadOnly}" Value="True"/>
                                        <Condition Binding="{Binding IsChecked, ElementName=_cboShowAll}" Value="False"/>
                                    </MultiDataTrigger.Conditions>

                                    <MultiDataTrigger.Setters>
                                        <Setter Property="Visibility" Value="Collapsed"/>
                                    </MultiDataTrigger.Setters>
                                </MultiDataTrigger>

                                <DataTrigger Binding="{Binding IsReadOnly}" Value="True">
                                    <Setter Property="Background" Value="Gray"/>
                                    <Setter Property="IsEnabled" Value="False" />
                                </DataTrigger>

                            </Style.Triggers>
                        </Style>

                    </DataGrid.RowStyle>
                    <DataGrid.Resources>
                        <Style x:Name="CenterContent" TargetType="{x:Type TextBlock}">
                            <Setter Property="VerticalAlignment" Value="Center" />
                            <Setter Property="HorizontalAlignment" Value="Center" />
                        </Style>
                        <Style TargetType="{x:Type DataGridColumnHeader}">
                            <Style.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="ToolTip" Value="{Binding Column.(ToolTipService.ToolTip), RelativeSource={RelativeSource Self}}"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>

                        <Image x:Name="rowHeaderImage"
                       x:Key="rowHeaderTemplate"
                       VerticalAlignment="Center"
                       HorizontalAlignment="Center"
                       Width="64"                       
                       Margin="1,0">
                            <Image.Style>
                                <Style TargetType="Image">
                                    <Style.Triggers>
                                        <DataTrigger  Binding="{Binding IsReadOnly}" Value="true">
                                            <Setter Property="Source" Value="/Resources/access.ico"/>
                                        </DataTrigger >
                                        <DataTrigger  Binding="{Binding IsReadOnly}" Value="false">
                                            <Setter Property="Source" Value="/Resources/no_access.jpg"/>
                                        </DataTrigger >                                        
                                    </Style.Triggers>


                                </Style>
                            </Image.Style>
                        </Image>

                    </DataGrid.Resources>
                    <DataGrid.ItemBindingGroup>
                        <BindingGroup/>
                    </DataGrid.ItemBindingGroup>
                    <DataGrid.Columns>

                        <!--This is the Name column-->
                        <DataGridTemplateColumn Header="Name" ToolTipService.ToolTip="Global variable name/ID" MinWidth ="200" Width="2*">

                            <DataGridTemplateColumn.CellStyle>
                                <Style TargetType="DataGridCell">
                                    <Setter Property="Background" Value="Gray"></Setter>
                                </Style>
                            </DataGridTemplateColumn.CellStyle>

                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>

                                    <TextBox Text="{Binding Name}" IsReadOnly="True" Background="Transparent" ToolTip="{Binding Name}"/>

                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>

                        <!--This is the Site column-->
                        <!--<DataGridTemplateColumn Header="Site" ToolTipService.ToolTip="Site" Width="1*" MaxWidth ="30" IsReadOnly="False" Visibility="Hidden">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBox TextAlignment="Center" Text="{Binding Site, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" IsReadOnly="{Binding IsReadOnly}" Background="Transparent"/>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>-->

                        <!--This is the Slot column-->
                        <!--<DataGridTemplateColumn Header="Slot" ToolTipService.ToolTip="Slot" Width="1*" MaxWidth ="30" IsReadOnly="False" Visibility="Hidden">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>

                                        <TextBox TextAlignment="Center" Text="{Binding Slot, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" IsReadOnly="{Binding IsReadOnly}" Background="Transparent"/>

                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>-->

                        <!--This is the High Limit column-->
                        <!--<DataGridTemplateColumn Header="High Limit" ToolTipService.ToolTip="High limit" MaxWidth="125" Width="1*" Visibility="Hidden">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBox TextAlignment="Center" Text="{Binding HiLimit, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" IsReadOnly="{Binding IsReadOnly}" Background="Transparent"/>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>-->

                        <!--This is the Low Limit column-->
                        <DataGridTemplateColumn Header="Value" MinWidth="200" ToolTipService.ToolTip="Global variable programmed value" Width="3*">
                            <DataGridTemplateColumn.HeaderStyle>
                                <Style TargetType="DataGridColumnHeader">
                                    <Setter Property="HorizontalContentAlignment"
                                            Value="Center" />
                                </Style>
                            </DataGridTemplateColumn.HeaderStyle>

                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>                          

                                    <!--By default, allow the user to free edit the combobox.  
                                        This allows backwards compatibility with existing TP settings-->

                                    <ContentControl Content="{Binding}">
                                        <ContentControl.Style>

                                            <Style TargetType="ContentControl">

                                                <Style.Triggers>
                                                    <DataTrigger Binding="{Binding HasRange}" Value="true">
                                                        <Setter Property="ContentTemplate">
                                                            <Setter.Value>
                                                                <DataTemplate>
                                                                    <ComboBox                                                                                                                                            
                                                                      ItemsSource="{Binding Range, Mode=OneWay}"                                                                      
                                                                      MinWidth="50"                                                                      
                                                                      Text="{Binding ProgrammedValue,                                                                        
                                                                       UpdateSourceTrigger=LostFocus, 
                                                                       ValidatesOnDataErrors=True, 
                                                                       NotifyOnValidationError=True}"                                                                      
                                                                      Style="{StaticResource RangeStyleNormal}"
                                                                      Width="auto" >

                                                                    </ComboBox>



                                                                </DataTemplate>
                                                            </Setter.Value>
                                                        </Setter>
                                                    </DataTrigger>

                                                    <DataTrigger Binding="{Binding HasRange}" Value="false">
                                                        <Setter Property="ContentTemplate">
                                                            <Setter.Value>
                                                                <DataTemplate>
                                                                    <TextBox 
                                                                        HorizontalAlignment="Stretch"                                               
                                                                        Background="Transparent" 
                                                                        BorderThickness="0"
                                                                        MinWidth="50"                                                                        
                                                                        Text="{Binding ProgrammedValue, Mode=TwoWay,                                                                        
                                                                        UpdateSourceTrigger=LostFocus,
                                                                        ValidatesOnDataErrors=True, 
                                                                        NotifyOnValidationError=True
                                                                        }"                                                                        
                                                                        TextAlignment="Center"
                                                                        Width="Auto">
                                                                    </TextBox>
                                                                </DataTemplate>
                                                            </Setter.Value>
                                                        </Setter>
                                                    </DataTrigger>

                                                </Style.Triggers>
                                            </Style>
                                        </ContentControl.Style>
                                    </ContentControl>

                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>

                        <!--This is the Low Limit column-->
                        <!--<DataGridTemplateColumn Header="Low Limit" ToolTipService.ToolTip="Lower limit" Width="1*" MaxWidth="125" Visibility="Hidden">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBox TextAlignment="Center" Text="{Binding LowLimit, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" IsReadOnly="{Binding IsReadOnly}" Background="Transparent"/>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>-->

                        <!--This is the Fleet column-->
                        <DataGridTemplateColumn Header="Fleet Variable" ToolTipService.ToolTip="Global variable scope" MinWidth ="75" Width="1*">
                            <DataGridTemplateColumn.CellStyle>
                                <Style TargetType="DataGridCell">
                                    <Setter Property="Background" Value="Gray"></Setter>
                                </Style>

                            </DataGridTemplateColumn.CellStyle>
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>

                                    <TextBox Text="{Binding IsFleet}" IsReadOnly="True" Background="Transparent" ToolTip="{Binding IsFleet}"/>

                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <!--<DataGridCheckBoxColumn Header="Is fleet" Width="1*" MinWidth="50"  ToolTipService.ToolTip="Variable of fleet scope" Binding="{Binding IsFleet}" IsReadOnly="True">

                        </DataGridCheckBoxColumn>-->
                    </DataGrid.Columns>
                </DataGrid>
            </DockPanel>
            <!--These are the buttons at the bottom of the control-->
            <Grid Grid.Row="3">
                <StackPanel Height="40" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,2" Width="auto">
                    <Label Height="25" HorizontalAlignment="Left" Width="40" Margin="5,2,3,0">Filter:</Label>
                    <TextBox x:Name="_tboFilter" HorizontalAlignment="Left" Height="25" Margin="0,3,0,0" Width="200" MaxLength="40" MaxLines="1" MinLines="1" AcceptsTab="True" TextAlignment="Left" Text="{Binding FilterString, UpdateSourceTrigger=PropertyChanged}" />
                    <CheckBox Name="_cboShowAll" HorizontalAlignment="Left" Height="25" IsChecked="False" Margin="15,7,0,0">
                        <TextBlock Text="Show all" />
                    </CheckBox>
                </StackPanel>

                <StackPanel Height="40" Width="auto" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,2">
                    <Button x:Name="btnTestError" HorizontalAlignment="Right"  Height="25" Width="60" Content="TestError" ToolTip="Fire error events for logging" Margin="0,0,3,0" Command="{Binding TestErrorButtonCommand}" Visibility="{Binding EnableQADebuggingTools}" />
                    <!--<Button x:Name="btnApply" Height="25" Width="50" Content="Apply" ToolTip="Apply changes" Margin="0,3,3,0" Click="btnApply_Click" IsEnabled="{Binding HasDataChanged}" />-->
                    <Button x:Name="btnApply" HorizontalAlignment="Right" Height="25" Width="50" Content="Apply" ToolTip="Apply changes" Margin="0,0,3,0" Command="{Binding ApplyChangesButtonCommand}" >
                        <Button.Style>
                            <Style TargetType="Button">
                                <Setter Property="IsEnabled" Value="False"/>
                                <Style.Triggers>
                                    <!-- Button enabled if the following conditions is met:  HasDataChanged is true -->
                                    <MultiDataTrigger>
                                        <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding HasDataChanged}" Value="True"/>
                                            <Condition Binding="{Binding HasWriteAccess}" Value="True"/>
                                        </MultiDataTrigger.Conditions>
                                        <Setter Property="IsEnabled" Value="True"/>
                                    </MultiDataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Button.Style>
                    </Button>

                    <!--<Button x:Name="btnCancel" Height="25" Width="50" Content="Cancel" Margin="0,3,3,0" ToolTip="Revert to TP values" Click="btnCancel_Click" Command="{Binding CancelButtonCommand}"/>-->
                    <Button x:Name="btnCancel" HorizontalAlignment="Right" Height="25" Width="50" Content="Cancel" Margin="0,0,3,0" ToolTip="Revert to TP values" Click="btnCancel_Click"/>
                </StackPanel>
            </Grid>
        </Grid>
    </GroupBox>

</UserControl>

IDataErrorInfo这样实现:

public string this[string columnName]
{
    get
    {
        string result = null;

        try
        {
            if (columnName == "ProgrammedValue")
            {
                //Validation routine: data can not be empty field.

                if (this.HasRange)
                {
                    // This is a combobox
                    if (ProgrammedValue == null)
                    {
                        result = "Field must have selection";
                    }
                    else
                    {
                        if (Range != null && !Range.Contains(ProgrammedValue) )
                        {
                            result = "Field must have selection";
                        }
                    }
                }
                else
                {
                    // This is a text box:
                    if (string.IsNullOrEmpty(ProgrammedValue))
                    {
                        result = "Field can not be empty.";
                    }
                }
            }
        }
        catch (Exception ex)
        {
            // Eat the exception.. really should never get here, but if there is an exception
            result = null;
        }

        return result;
    }
}

在我的模型中,定义了绑定属性:

public string ProgrammedValue
{
    get
    {
        return _programmedValue;
    }

    set
    {
        _programmedValue = value;
        RaisePropertyChanged("ProgrammedLimit");
    }
}


public List<string> Range
{
    get
    {
        return _range;
    }

    set
    {
        _range = value;
        RaisePropertyChanged("Range");
    }
}

public bool HasRange
{
    get
    {
        return _hasRange;
    }

    set
    {
        _hasRange = value;
        RaisePropertyChanged("HasRange");
    }
}

RangeStyleNormal定义:

<UserControl.Resources>

        <Style x:Key="RangeStyleNormal" TargetType="ComboBox">            
            <Setter Property="HorizontalContentAlignment" Value="center"/>       
            <EventSetter Event="SelectionChanged" Handler="ComboBox_SelectionChanged"></EventSetter>
            <Setter Property="IsEditable" Value="False" />
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="IsTextSearchCaseSensitive" Value="True"/>
        </Style>      

    </UserControl.Resources>

如前所述,当我从文本框属性中删除ValidatesOnDataErrors和NotifyOnValidationError时,组合框的工作方式就像一个超级按钮。我知道我指定的验证例程可以正常工作。我希望这里的人可以看到我犯的一些明显的编码错误,并解释我做错了什么以及如何纠正它。预先感谢。

0 个答案:

没有答案