【问题标题】:Win8 ComboBox IsDropDownOpen + Visibility causes Glitched UIWin8 ComboBox IsDropDownOpen + Visibility 导致 UI 出现故障
【发布时间】:2013-02-27 23:40:39
【问题描述】:

我正在使用 XAML 和 C# 开发一个 Windows 8 应用程序。 我的 ComboBox 有问题,并且有一个简单的示例来演示它。

  1. 将以下内容添加到布局感知页面(新 BasicPage)

    <ComboBox x:Name="comboBox1" DropDownClosed="comboBox1_DropDownClosed" Visibility="Collapsed" HorizontalAlignment="Left" Margin="179,217,0,0" Grid.Row="1" VerticalAlignment="Top" Width="998" Height="51">
        <x:String>Option 1</x:String>
        <x:String>Option 2</x:String>
        <x:String>Option 3</x:String>
    </ComboBox>
    <Button Click="Button_Click" Margin="585,130,0,416" Grid.Row="1" Height="82" Width="154">
        <Viewbox>
            <TextBlock Text="Press Me" />
        </Viewbox>
    </Button>
    
  2. 将此添加到页面的 CodeBehind

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        comboBox1.Visibility = Windows.UI.Xaml.Visibility.Visible;
        comboBox1.IsDropDownOpen = true;
    }
    
    private void comboBox1_DropDownClosed(object sender, object e)
    {
        comboBox1.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
    }
    

预期: 当按钮被按下时,ComboBox 应该出现并且选项应该展开,允许用户选择一个。一旦用户选择了一个选项,ComboBox 就会消失。

实际结果: 按下按钮时,没有任何反应。如果第二次按下该按钮,ComboBox 会出现故障状态,并且应用程序本质上是无响应的。 (所有输入都指向 ComboBox,它永远不会关闭。

注意:DropDownClosed 事件在 Button_Click 事件之后立即触发。删除事件处理程序不会改变任何事情,但有趣的是 DropDownClosed 事件正在触发。

被拒绝的解决方案: 有人建议我在Visibility 更改生效后使用Dispatcher.RunAsync 设置IsDropDownOpen。这似乎是一种竞争条件,因为它只在某些的时候起作用。 如果有办法确认 ComboBox 已呈现可见,将此检查添加到 RunAsync 方法可以解决问题。

作为一种解决方法,我目前将 Dispatcher.RunAsync 延迟 200 毫秒,这是一个烦人的解决方法。还有其他想法吗?

【问题讨论】:

  • Metro中没有XamlTriggers吗?将 UI 逻辑拖到后面的代码中似乎很奇怪

标签: c# multithreading xaml microsoft-metro windows-store-apps


【解决方案1】:

你是对的,在尝试设置 IsDropDownOpen 之前,你需要确保 comboBox1 实际呈现可见。方法是通过Dispatcher拨打第二个电话:

private void Button_Click(object sender, RoutedEventArgs e)
{
    comboBox1.Visibility = Windows.UI.Xaml.Visibility.Visible;
    Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => comboBox1.IsDropDownOpen = true);
}

【讨论】:

  • +1。这似乎对我有用,并且可能是比我更好的答案。我也很好奇为什么这个问题首先存在。关于您的解决方案为何有效的任何进一步想法,或者您至少可以向我指出一些文档吗?
  • 这并不能解决我的问题。我熟悉Dispatcher.RunAsync,但我相信这个解决方案是不完整的。显然,设置Visibility 属性不会在ComboBox 渲染后同步返回,我怀疑渲染ComboBox 的任务是否保证在我对IsDropDownOpen 的分派调用之前完成。我误解了吗?也许有一种方法可以确认 ComboBox 已被渲染:要么对 comboBox1/Frame 进行测试,要么更好地理解 UI 任务调度,或者从渲染事件或类似事件中设置 IsDropDownOpen
  • @chuex 不幸的是,我没有遇到任何关于该主题的文档,尽管我也会对此非常感兴趣。我以前在类似的情况下使用过这种方法,它总是对我有用。我没有任何明确的证据,但我的理解是Dispatcher.RunAsync 在 UI 线程上安排调用,而CoreDispatcherPriority.Normal 意味着它将按照预定的顺序进行处理。假设Visibility setter 还以正常优先级将渲染调度到 UI 线程,则调用应始终以正确的顺序处理。
  • @Nathan 是否存在对您不起作用的情况,即您是否遇到过竞争情况?还是您只是怀疑该解决方案将始终正常工作?我真的很想知道,因为正如我在上面的评论中已经提到的那样,这种方法在类似情况下对我有用。
  • 上述解决方案适用于简单的案例,但在我的实际应用中不起作用。也许我的简单例子太简单了。在我的实际应用中,尽管使用了Dispatcher.RunAsync,但有时会出现故障 UI,就好像我没有使用过Dispatcher.RunAsync。但是,正如我所提到的,在设置IsDropDownOpen 之前使用await Task.Delay(200); 延迟调度方法之后,故障还没有发生。这不是强烈暗示竞争条件吗?
【解决方案2】:

多么讨厌的错误啊?

一个简单的解决方法是不使用 Visibility 属性,而是使用 Opacity。它按预期工作:

   <ComboBox x:Name="comboBox1" DropDownClosed="comboBox1_DropDownClosed" Opacity="0" HorizontalAlignment="Left" Margin="179,217,0,0" Grid.Row="1" VerticalAlignment="Top" Width="998" Height="51">
        <x:String>Option 1</x:String>
        <x:String>Option 2</x:String>
        <x:String>Option 3</x:String>
    </ComboBox>


    private void Button_Click(object sender, RoutedEventArgs e) {
        comboBox1.Opacity = 1;
        comboBox1.IsDropDownOpen = true;
    }

    private void comboBox1_DropDownClosed(object sender, object e) {
        comboBox1.Opacity = 0;
    }

干杯!

【讨论】:

  • 内森会告诉他这个解决方法是否适合他。 Visibility = CollapsedOpacity = 0 并不完全相同,但可能足够接近。无论如何,我更感兴趣的是找到原始问题的根源,而不是寻找解决方法:)
  • 谢谢安德烈,我认为这可能暂时有效。当然,它迫使我以其他方式隐藏控件,因为我仍然可以点击一个透明的 ComboBox,并且它会导致自动调整大小 Grids 以保留空间。我同意 Damir 的观点,我希望有一个工作的 Visibility,但这似乎是 200 毫秒延迟的一个很好的替代方案。
【解决方案3】:

我已经在我的台式机和 Surface 设备上测试了以下内容,它似乎一直都能正常工作。这是延迟设置IsDropDownOpen 的变体。我了解您可能已经尝试了一些产生竞争条件的变体。我没有看到比赛条件,所以希望它也适用于你。

// need this for Task
using System.Threading.Tasks;
...

// note async keyword added to function signature
async private void Button_Click(object sender, RoutedEventArgs e)
{
    comboBox1.Visibility = Windows.UI.Xaml.Visibility.Visible;
    // add small delay before opening dropdown
    await Task.Delay(1);
    comboBox1.IsDropDownOpen = true;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-25
    • 2021-01-29
    • 2020-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多