【问题标题】:ContentControl switch between two buttonsContentControl 在两个按钮之间切换
【发布时间】:2015-06-06 16:31:09
【问题描述】:

我整晚都在为此头疼。我想要做的就是拥有一个内容控件,它可以根据 ViewModel 中的布尔值在显示两个不同按钮之间切换。

基本上我有一个任务在后台运行,带有一个取消按钮。一旦您点击取消并且任务停止,取消按钮应更改为后退按钮。这是两个独立的按钮元素,而不仅仅是更改单个按钮的属性。我正在使用 MahApps TransitioningContentControl,所以我希望能够使用过渡。

如果这大部分都可以在 XAML 中完成,我会很惊讶。我真的不想为应该很简单的事情添加一堆样板代码。

编辑:这是代码的 sn-p。没什么,因为我只是删除了所有不起作用的东西。

<Controls:TransitioningContentControl x:Name="CancelBackButtonControl" HorizontalAlignment="Left" VerticalAlignment="Top" Width="80" Height="80">
    <Canvas>
        <Button x:Name="CancelButton" HorizontalAlignment="Left" VerticalAlignment="Top" Width="80" Style="{DynamicResource MetroCircleButtonStyle}" Height="80" IsCancel="True" Content="{StaticResource appbar_close}" BorderBrush="Black" Command="{Binding CancelCommand}"/>
        <Button x:Name="GoBackButton" HorizontalAlignment="Left" VerticalAlignment="Top" Width="80" Style="{DynamicResource MetroCircleButtonStyle}" Height="80" IsCancel="True" Content="{StaticResource appbar_arrow_left}" BorderBrush="Black" Command="{Binding GoBackCommand}"/>
    </Canvas>
</Controls:TransitioningContentControl>

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    实现这一点的方法数量惊人(这就是 WPF 的美妙之处);但是,在我个人看来,如果您只是使用从ToggleButton 派生的控件(例如RadioButton),那么您的工作就会更少“违背常规”,特别是因为您希望它全部在 XAML 中完成。重要的是不要通过视觉来考虑任何控件,而是通过它们的功能来考虑。我以前用RadioButton 做过一些不可思议的事情,所以这是我要演示的第一件事;但是,此答案的后半部分包含常规按钮方法。

    以下是两种方法的完整示例(RadioButtonButton),完全在 XAML 中完成:

    预览:

    代码:

    <Window x:Class="Sample.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <Style TargetType="{x:Type RadioButton}" x:Key="FlatRadioButtonStyle">
                <Setter Property="Width" Value="100"/>
                <Setter Property="Background" Value="#FF346FD6"/>
                <Setter Property="Foreground" Value="White"/>
                <Setter Property="Padding" Value="5,2,5,3"/>
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Setter Property="HorizontalAlignment" Value="Center"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type RadioButton}">
                            <Border Background="{TemplateBinding Background}"
                                    Width="{TemplateBinding Width}"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    VerticalAlignment="{TemplateBinding VerticalAlignment}"
                                    HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
                                <ContentPresenter Content="{TemplateBinding Content}"
                                                  Margin="{TemplateBinding Padding}"
                                                  HorizontalAlignment="Center"
                                                  VerticalAlignment="Center"/>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Background" Value="#FF6696E9"/>
                                </Trigger>
                                <Trigger Property="IsChecked" Value="True">
                                    <Setter Property="Visibility" Value="Collapsed"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Window.Resources>
    
        <Grid>
            <RadioButton x:Name="BackButton" Content="Back">
                <RadioButton.Style>
                    <Style TargetType="{x:Type RadioButton}" BasedOn="{StaticResource FlatRadioButtonStyle}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding IsChecked, ElementName=CancelButton}" Value="True">
                                <Setter Property="Visibility" Value="Visible"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </RadioButton.Style>
            </RadioButton>
            <RadioButton x:Name="CancelButton" Content="Cancel" IsChecked="True">
                <RadioButton.Style>
                    <Style TargetType="{x:Type RadioButton}" BasedOn="{StaticResource FlatRadioButtonStyle}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding IsChecked, ElementName=BackButton}" Value="True">
                                <Setter Property="Visibility" Value="Visible"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </RadioButton.Style>
            </RadioButton>
        </Grid>
    </Window>
    

    大部分资源样式代码是视觉样式和模板,专注于使单选按钮看起来像常规按钮,所以它不是很重要。我们将关注的领域是Triggers。您会注意到,在资源样式中,我确保当 RadioButton 被选中时,它会崩溃。但是,在每个按钮的本地样式中,我确保当另一个RadioButton 被选中时,它使当前的RadioButton 可见。这必须以本地样式完成,因为我们需要将ElementName 传递给Binding。因此,当一个RadioButton 被选中时,它会折叠并使另一个RadioButton 可见。您还会注意到,我选中了我希望默认隐藏的按钮。显然,您可以使用绑定将其连接起来。

    类似的方法可以应用于常规按钮:

    预览:

    代码:

    <Window x:Class="Sample.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">   
        <Window.Resources>
            <Style TargetType="{x:Type Button}" x:Key="ToggleButtonStyle">
                <Setter Property="Width" Value="100"/>
                <Setter Property="HorizontalAlignment" Value="Center"/>
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Style.Triggers>
                    <Trigger Property="IsFocused" Value="True">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>
        <Grid>
            <Button x:Name="CancelButton" Content="Cancel">
                <Button.Style>
                    <Style TargetType="{x:Type Button}" BasedOn="{StaticResource ToggleButtonStyle}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding IsFocused, ElementName=BackButton}" Value="True">
                                <Setter Property="Visibility" Value="Visible"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>
            <Button x:Name="BackButton" Content="Back">
                <Button.Style>
                    <Style TargetType="{x:Type Button}" BasedOn="{StaticResource ToggleButtonStyle}">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding IsFocused, ElementName=CancelButton}" Value="True">
                                <Setter Property="Visibility" Value="Visible"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>
        </Grid>
    </Window>
    

    在这里,我使用的是IsFocused,而不是IsChecked。您可以通过使用EventTriggerClick 事件来完成类似的事情......但是,还有更多工作。 IsPressed 可能看起来是一个不错的候选者,但问题是一旦你按下按钮,另一个几乎会立即出现,而那个将几乎瞬间将IsPressed 设置为true。因此,您最终会遇到这种似乎什么都没有发生的循环行为。请注意,我使用Grid 将这些按钮放在彼此的顶部,而我希望默认显示的按钮位于顶部,这样我就不必担心默认的可见性或焦点。但是,您可以使用任何其他面板,只需将您要隐藏的按钮的Visibility 默认设置为Collapsed

    如果您不想使用多个控件(在这种情况下为两个按钮),您还可以通过DataTrigger 根据条件设置按钮的Content 属性以显示不同的文本。您只需确保正确处理Command

    【讨论】:

    • 不错的答案。 +1。不相关的问题:您使用什么软件制作预览版?
    • @HighCore 我使用 LICEcap (cockos.com/licecap) 和 Screen To Gif (screentogif.codeplex.com)。 SG 实际上是在 WPF 中创建的,但在某些情况下不太稳定。
    • 谢谢,我试试看;)
    • 这是一些非常好的信息,但@JamesHarcourt 的回答正是我所需要的。还是谢谢!
    【解决方案2】:

    由于您要求使用 XAML 唯一方法来基于 ViewModel 属性切换内容控件中的按钮,因此如何:

    首先定义按钮样式以将两个按钮路由到同一个事件:

    <Style x:Key="buttonClickButton" TargetType="Button">
        <EventSetter Event="Click"  Handler="Button_Click"/>
    </Style>
    

    然后定义您的内容控件样式,使用基于名为“IsCancelButton”(布尔)的 ViewModel 属性切换内容的数据触发器:

    <Style x:Key="ButtonSwitchContentCtrl" TargetType="ContentControl">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsCancelButton}" Value="True">
                <Setter Property="Content">
                    <Setter.Value>
                        <Button x:Name="CancelButton" Style="{StaticResource buttonClickButton}" 
                                HorizontalAlignment="Left" VerticalAlignment="Top" 
                                Width="80"  Height="80" IsCancel="True" Content="Cancel Button" BorderBrush="Black" />
                    </Setter.Value>
                </Setter>
            </DataTrigger>
            <DataTrigger Binding="{Binding IsCancelButton}" Value="False">
                <Setter Property="Content">
                    <Setter.Value>
                        <Button x:Name="GoBackButton" Style="{StaticResource buttonClickButton}" 
                                HorizontalAlignment="Left" VerticalAlignment="Top" 
                                Width="80"  Height="80" IsCancel="True" Content="Go Back Button" BorderBrush="Black" />
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
    </Style>
    

    然后让你的按钮看起来像这样:

    <ContentControl Style="{StaticResource ButtonSwitchContentCtrl}"/>
    

    【讨论】:

    • 这正是我需要的!如果可以,我会拥抱你。
    【解决方案3】:

    您只需一个按钮(例如,在 XAML 中声明为 Name="btnCancel")和使用 Lambda 样式事件订阅的 C# 代码隐藏中的简单代码 sn-p 即可实现此类功能,例如:

        btnCancel.Click+=(s,e)=>{
        if (btnCancel.Text=="Cancel")
        {
          // SOME CODE CORRESPONDING TO "CANCEL" CLICK EVENT
          btnCancel.Text ="GoBack"
        }
        else 
        {
          // CORRESPONDING TO "GoBack" CLICK EVENT
          btnCancel.Text ="Cancel"
        }
    }
    

    如果您想在 Button 上使用一些图形内容(图像),请使用该 Button 的 Tag 属性而不是 Text,并以编程方式在图像之间切换。

    其他一些注意事项:您所描述的业务逻辑可以使用 DataTriggers 在 XAML 中实现(就像其他人所做的那样),但不是为 2-Buttons 解决方案编写兆吨的 XAML,而是实现一个单一的-按钮解决方案,或者像这个,或者使用 .NET ToggleButton 类(回复:https://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.togglebutton%28v=vs.110%29.aspx);此外,它可能是一个样式正确的CheckBox 控件。

    无论如何,除了更改 Button 的视觉状态之外,您很可能还需要事件处理程序来完成一些实际工作,这样紧凑的 Lambda 样式事件订阅会很方便。

    希望这可能会有所帮助。最好的问候,

    【讨论】:

    • 虽然这可能有效 (+1),但它不是一种非常 XAML/WPF 的做事方式。这是在 WinForms GUI 中要做的事情。
    猜你喜欢
    • 1970-01-01
    • 2019-02-27
    • 1970-01-01
    • 2018-09-24
    • 2015-04-15
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多