当我更改状态消息或显示等待屏幕时,WPF ListBox滚动到顶部 [英] WPF ListBox scrolls to top when I change status message or show wait screen

查看:86
本文介绍了当我更改状态消息或显示等待屏幕时,WPF ListBox滚动到顶部的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个名为媒体助手的开源应用程序 [

I''m developing an opensource application named Media Assistant[^]. I used a ListBox to show the library. ItemsSource is bound to a list of LibraryItem. Here is the XALM.

<listbox name="Tree" dockpanel.dock="Top">
    ItemsSource="{Binding DataSource.OrderedLibraryItems}" 
    removed="{StaticResource LibraryBackground}"
    Width="220" HorizontalAlignment="Left"
    BorderThickness="0"
    VirtualizingStackPanel.IsVirtualizing="True"
    VirtualizingStackPanel.VirtualizationMode="Standard"
    ScrollViewer.IsDeferredScrollingEnabled="True"
    ItemTemplate="{StaticResource ListLibraryItemTemplate}"
    SelectionMode="Single"
    MouseDoubleClick="HandleMouseDoubleClick"
/>


问题是当我使用Dispatcher在线程底部的窗口底部显示任何状态消息时.


The problem is when I show any status message at the bottom of my window from a thread by using Dispatcher.

Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,new ParameterizedThreadStart(action), state);



列表框滚动到顶部.如果我不显示任何状态消息,则说明一切正常.数据上下文或列表项或焦点尚未更改.我找不到执行此操作的任何原因.当我显示任何等待屏幕(非模式窗口)时,就会发生这种情况.我无法在其他项目中重新创建它.
此处 ^ ].
您可以通过取消注释BackgroundScanner类中的SetStatusMessage方法的return语句来轻松地重新创建它.这是代码.



The ListBox scrolls to at the top. If I don''t show any status message then it works just fine. The datacontext or list items or focus has not been changed. I could not found any reason why it''s doing that. It happens when I display any wait screen which is a non modal window. I could not recreate it in a different project. Here is the source code of Media Assistant[^].
You can easily re-create it by un-commenting the return statement of method SetStatusMessage at BackgroundScanner class. Here is the code.

protected void SetStatusMessage(string message)
{
//Note: Because of status message Library scrolled to top. I don't know why.
    return;
    StatusMessageService.SetStatusMessage(message);
}



滚动时令人不安,如果后台扫描程序更改了状态栏消息,它会突然滚动到顶部.请让我知道如何解决此问题.



It''s disturbing when I scrolling and suddenly it scrolls to top if the background scanner changes the status bar message. Please let me know how to fix it.

推荐答案

我找到了背后的原因,因此找到了解决方案.
我使用了DockPanel来布局我的UI.我将状态栏放在底部,列表框在左侧,其他项目在中间和顶部.我的StatusBar中有一个TextBlock,它的width和Height设置为Auto.因此,当我更改StatusBar TextBlock的文本时,将重新计算其宽度和高度,而其父级将重新计算其布局.因此,列表框将被调用到度量和排列".即使其大小未更改,它也会将其滚动位置重置为顶部.仅当我在ListBox上使用ScrollViewer.CanContentScroll ="True"时,它才会发生.默认情况下为True.因此,即使我没有设置该值,它也正在重置滚动位置.如果我通过使用ScrollViewer.CanContentScroll ="False"禁用它,那么它将正常工作.

I found the reason behind this, so the solution.
I used a DockPanel to layout my UI. I put my status bar at the bottom, the ListBox on the Left and other items are on middle and top. There is a TextBlock in my StatusBar which has width and Height set to Auto. So, when I changed text of my StatusBar TextBlock it''s width and height gets recalculated and It''s parent''s recalculates it''s layout. Hence the ListBox gets invoked to Measures and Arrange. Even though it''s size does not gets changed it resets it''s scroll position to top. It happens only if I use ScrollViewer.CanContentScroll="True" at the ListBox. By default it is True. So, even though I did not set this value It was resetting the scroll position. If I disable it by using ScrollViewer.CanContentScroll="False" then it works fine.

<listbox name="Tree" dockpanel.dock="Top">
        ItemsSource="{Binding DataSource.OrderedLibraryItems}" 
        Background="{StaticResource LibraryBackground}"
        Width="220" HorizontalAlignment="Left"
        BorderThickness="0"
        VirtualizingStackPanel.IsVirtualizing="True"
        VirtualizingStackPanel.VirtualizationMode="Standard"
        ScrollViewer.IsDeferredScrollingEnabled="True"
        ScrollViewer.CanContentScroll="False"
        ItemTemplate="{StaticResource ListLibraryItemTemplate}"
        SelectionMode="Single"
        MouseDoubleClick="HandleMouseDoubleClick"
    /></listbox>



但是设置ScrollViewer.CanContentScroll ="False"会禁用虚拟化,并且我想对我的ListBox使用虚拟化,因此我将固定的Height和Width设置为TextBlock.因此,如果我更改状态消息,则DockPanel不会重新排列其子级.

可能是ScrollViewer的一个错误.如果尺寸未更改,则不应更改滚动位置.



But setting ScrollViewer.CanContentScroll="False" disables virtualization and I want to use virtualization to my ListBox so I set fixed Height and Width to the TextBlock. So, the DockPanel does not re-arrange it''s children if I change the status message.

May be it''s a bug at ScrollViewer. It should not change the scroll position if the size has not changed.


很抱歉,无法立即解决您的问题;从您的问题和代码示例中,尚不清楚发生了什么;请参阅我对这个问题的评论.

但是,有些事情您应该首先重新考虑.这行看起来很有趣:

Sorry for not addressing your issue immediately; from your question and code sample it''s not clear what''s going on; please see my comment to the question.

However, there is something you should re-thing first. This line looks just funny:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background,new ParameterizedThreadStart(action), state);



看起来很吓人.它使我怀疑您不知道Dispatcher ispatcher.BeginInvoke或Dispatcher.Invoke是什么.否则,为什么要使用这种奇怪的ParameterizedThreadStart委托类型?如果您看这行代码中的操作",所有的麻烦都可能变得很明显.

有趣的是,您可以正式使用此委托类型,但这与调用的目的无关.使用Dispatcher进行调用是一种将调用从某个任意线程委托给一个UI线程(UI线程,仅此而已)的一种方法.

因此,您需要使用属性System.Windows.Threading.DispatcherObject.Dispatcher(作为Control is DispatcherObject)的Control的调度程序实例.它可能是您的状态栏,但是究竟要进行什么控制并不重要.它应该是通过UI线程创建并添加到您的UI的控件的某些实例.

Dispatcher.BeginInvokeDispatcher.Invoke中使用的委托类型可以是任何带有引用或值类型的按值参数的委托类型(也可以使用out或ref参数,但这没有任何意义).该调用将获取调用所需的所有数据-处理程序入口点,声明方法的对象实例的此"引用(如果有的话,静态方法为null),调用参数的所有值-和队列将此数据存储到UI线程可访问的某个队列中. UI线程不会使用此UI数据来完成调用本身,而永远不会通过调用Invoke eginInvoke的线程来完成.

在我过去的答案中,您将找到有关其工作原理的详细说明和代码示例:
Control.Invoke()与Control.BeginInvoke() [ ^ ],
Treeview Scanner和MD5的问题 [如何获取keydown事件在vb.net中的不同线程上操作 [启用禁用+多线程后控件事件不会触发 [ ^ ].

—SA



It looks scary. It gives makes me suspect that you have no idea what Dispatcherispatcher.BeginInvoke or Dispatcher.Invoke; otherwise why would you use this strange ParameterizedThreadStart delegate type? All trouble may become apparent if you look at what is "action" in this line of code.

What''s funny, formally you can use this delegate type, but it has nothing to do with the purpose of invocation. The invocation with the use of Dispatcher is a way to delegate a call from some arbitrary thread to a UI thread (UI thread and nothing else).

For this reason, you need to use the dispatcher instance of the Control using the property System.Windows.Threading.DispatcherObject.Dispatcher (as Control is DispatcherObject). It could be your status bar, but it is not really important what exactly control is this. It should be some instance of the Control created and added to you UI by a UI thread.

The delegate type used in Dispatcher.BeginInvoke or Dispatcher.Invoke can be any delegate type with by-value parameters of reference or value types (out or ref parameters also could be used but it makes no sense). The call takes all data needed to a call — handler entry point, "this" reference of the instance of the object where the method is declared (if, any, null for static method), all the values of the call parameters — and queue this data to some queue which is accessible to the UI thread. The call itself is not done be the UI thread using this data from the UI and never by the thread calling InvokeeginInvoke.

You will find detailed explanation of how it works and code samples in my past answers:
Control.Invoke() vs. Control.BeginInvoke()[^],
Problem with Treeview Scanner And MD5[^].

See also more references on threading:
How to get a keydown event to operate on a different thread in vb.net[^],
Control events not firing after enable disable + multithreading[^].

—SA


这篇关于当我更改状态消息或显示等待屏幕时,WPF ListBox滚动到顶部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆