获取Hwnd.FromHwnd对话框的容器窗口

时间:2013-04-30 13:17:30

标签: wpf caliburn.micro

我正在使用Calibun Micro框架编写WPF应用程序。它实现了一个自动签名系统,该系统会在一段预定义的不活动时间后自动将用户签出应用程序。我使用here找到的方法检查是否不活动。

我在我的应用程序中创建对话框(使用windowmanager.showdialog(viewmodel)),这需要各种用户输入,我还需要在这些对话框上实现自动注销功能。我遇到的问题是我似乎无法从对话窗口中获取Hwnd详细信息。我目前在我的视图模型中执行以下操作:

public class BaseViewModel : Screen
{
    public BaseViewModel(User currentUser, IEventAggregator eventAggregator)
    {
        BaseEventAggregator = eventAggregator;
        CurrentUser = currentUser;

        InitializeTimer();
    }

    private void InitializeTimer()
    {
        var currentView = GetView();
        if (currentView as Window != null)
        {
            var windowSpecificOsMessageListener = HwndSource.FromHwnd(new WindowInteropHelper(currentView as Window).Handle);
        if (windowSpecificOsMessageListener != null)
        {
            windowSpecificOsMessageListener.AddHook(new HwndSourceHook(CallBackMethod));
        }

        _autoTimer = new Timer
            {
                Interval = Constants.Seconds * 1000
            };
        _autoTimer.Tick += delegate(object sender, EventArgs args)
            {
                _autoTimer.Stop();
                _autoTimer.Enabled = false;
                _autoTimer = null;
                BaseEventAggregator.Publish(new SignOutEventMessage());
            };
        _autoTimer.Enabled = true;
        }

    }

    private IntPtr CallBackMethod(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
    {
        //  Listening OS message to test whether it is a user activity
        if ((msg >= 0x0200 && msg <= 0x020A) || (msg <= 0x0106 && msg >= 0x00A0) || msg == 0x0021)
        {
            ResetAutoTimer();
        }
        else
        {
            // For debugging purpose
            // If this auto logoff does not work for some user activity, you can detect the integer code of that activity  using the following line.
            //Then All you need to do is adding this integer code to the above if condition.
            System.Diagnostics.Debug.WriteLine(msg.ToString());
        }
        return IntPtr.Zero;
    }
}

对于对话框执行InitializeTimer方法时,GetView的结果为null,因此自动注销计时器不会启动,应用程序也不会注销。

请告知我是否做错了。

1 个答案:

答案 0 :(得分:1)

您有两个潜在问题:

  1. 实例化视图模型时,尚未附加视图。 CM绑定系统启动并为您提供所有内容,但有几个步骤 - 在构造函数时间内绑定所有内容是不可能的。而是覆盖VM上的OnViewAttached

  2. 如果查看WindowManager实现,您会发现它实际上确保它为您绑定的VM解析的视图包含在窗口中。这意味着GetView()实际上返回给定VM的视图,该视图可能不一定是窗口。

  3. 根据您是创建UserControls还是实际Window控件,结果可能仍然不正确。我怀疑如果你排序问题1.你可能会遇到问题2.

    如果是这样,您只需要解析视图的Parent即可获得容纳它的Window

    编辑:要获取视图的父级,您可以使用表示逻辑元素的基类型FrameworkElement - 它具有Parent属性,该属性指向元素的逻辑父级

    您可以在OnViewAttached中使用以下内容:

    override OnViewAttached() 
    {
        var view = GetView();
    
        // Cast the window
        var window = view as Window;
    
        // If the control wasn't a window
        if(window == null)
        {
            // Cast to FrameworkElement
            var fe = view as FrameworkElement;
    
            // If it's null throw
            if(fe == null) throw new Exception("View was not present");
    
            // Otherwise try and cast its parent to a window
            window = fe.Parent as Window;
    
            // If no window, throw 
            if(window == null) throw new Exception("Window could not be found");     
        }
    
        // Do stuff
    }
    

    您可以将其作为IViewAware

    的扩展方法
    public static class IViewAwareExtensions
    {
        public static Window TryGetParentWindow(this IViewAware viewAware)
        {
            var view = viewAware.GetView();
    
            // Cast the window
            var window = view as Window;
    
            // If the control wasn't a window
            if(window == null)
            {
                // Cast to FrameworkElement
                var fe = view as FrameworkElement;
    
                // Return null if not found
                if(fe == null) return null;
    
                // Otherwise try and cast its parent to a window
                window = fe.Parent as Window;
    
                // If no window, return null
                if(window == null) return null;
            }
        }
    
        return window;
    }
    

    然后在OnViewAttached

    var window = this.TryGetParentWindow();