I use UsbHid, the author in the example creates everything in the View inside the form class, but I'm trying to transfer the code to the ViewModel

The problem is in these parts of the code:

private void Window_Loaded(object sender, RoutedEventArgs e) { HwndSource src = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); src.AddHook(new HwndSourceHook(WndProc)); } 

and

 protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); var handle = new WindowInteropHelper(this).Handle; usb.RegisterHandle(handle); } 

Here this form is needed so that the USB events are updated, but how can it be turned if the entire USB initialization occurs in the DataContext ?

 namespace MyApp.View { public partial class MainWindowView:MetroWindow { public MainWindowView() { DataContext = new DeveloperModeViewModel(); InitializeComponent(); } } } 

The code was taken from the article.

    2 answers 2

    As far as I understand, in your article at the moment the window is used for two purposes: to draw the UI and to receive messages about USB devices. The first use corresponds to V, the second to VM or even M.

    This means that you need to divide this functionality into different windows. In the model, you must open a separate, invisible window in a separate stream in which you will receive the necessary USB messages. An ordinary window should be used for display purposes.

    Now, in order to create an invisible functional window with a message loop, an ordinary WPF-ovsky window with Visible = false (did not check) can be triggered. But with a high degree of probability you will need an ordinary native window, which you will have to organize with P / Invoke. How to do this is best seen here .

    Removing too much of the class, it turns out this preparation:

     IntPtr hInstance = Process.GetCurrentProcess().Handle; WNDCLASS wndclass; wndclass.lpfnWndProc = (WndProc)((hWnd, message, wParam, lParam) => { IntPtr hdc; PAINTSTRUCT ps; RECT rect; usb.ParseMessages(...); switch ((WM)message) { case WM.DESTROY: Win32.PostQuitMessage(0); return IntPtr.Zero; } return Win32.DefWindowProc(hWnd, (WM)message, wParam, lParam); }); wndclass.hInstance = hInstance; wndclass.lpszClassName = "USBHelper"; ushort regResult = Win32.RegisterClass(ref wndclass); if (regResult == 0) throw new InvalidOperationExcetoon("Shouldn't happen"); IntPtr hwnd = Win32.CreateWindowEx( WindowStylesEx.WS_EX_OVERLAPPEDWINDOW, new IntPtr((int)(uint)regResult), "", // window caption WindowStyles.WS_OVERLAPPEDWINDOW, // window style Win32.CW_USEDEFAULT, // initial x position Win32.CW_USEDEFAULT, // initial y position Win32.CW_USEDEFAULT, // initial x size Win32.CW_USEDEFAULT, // initial y size IntPtr.Zero, // parent window handle IntPtr.Zero, // window menu handle hInstance, // program instance handle IntPtr.Zero); // creation parameters if (hwnd == IntPtr.Zero) { int lastError = Marshal.GetLastWin32Error(); throw new Win32Exception(lastError); } // ShowWindow не нужен, чтобы окно не показывалось // Win32.ShowWindow(hwnd, ShowWindowCommands.Normal); // Win32.UpdateWindow(hwnd); // цикл сообщений MSG msg; while (Win32.GetMessage(out msg, IntPtr.Zero, 0, 0) != 0) { Win32.TranslateMessage(ref msg); Win32.DispatchMessage(ref msg); } 

    P / Invoke-definitions of missing functions from the Win32 class look at the same http://www.pinvoke.net .

    • This is exactly what I would not like to do, it looks very crutch. But since there is no way out, you will have to do so - Fangog
    • @Fangog: The problem is actually that the USB messages (which are essentially model pieces) are delivered to the window (that is, to the UI level). This is somehow wrong. But this is a WinAPI problem, not yours. (Well, that is now yours too, of course.) - VladD
    • Why create another window if there is a WindowInteropHelper.RegisterHandle ? Yes, and the question was not this, just with the capture of messages from the author had no problems. - Raider
    • @Raider: Because in the framework of MVVM, the functionality of USB messages does not belong to View. Therefore, it can not be pulled in the View. The author had a problem, because the VM and the more so M do not have the right to know about the presence of V. at all. - VladD
    • @VladD For this, there is the concept of service and Dependency Injection. - Raider

    As part of the MVVM pattern, a VM object cannot know anything about a window; it goes beyond its area of ​​responsibility. The correct thing is to leave getting the handle in the class window.

    And now you can work with UsbHid in VM. By the way, why do you need two events?

     protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); ((ISomeViewModel)DataSource).Source = (HwndSource)PresentationSource.FromVisual(this); } 

    Just do not forget that the assignment of any property can occur several times and it must be correctly processed (i.e. it is necessary to remove the old hook before adding a new one, etc.).


    Alternatively, you can use the MVP pattern. Presenter, in contrast to the ViewModel, has the right to know about the form. So Presenter can subscribe to a SourceInitialized event.

    Or, to reuse code, it makes sense to try using behaviors (Behavior) :

     <Window ...> <i:Behaviors> <ctrl:UsbHidListener ViewModel="{Binding ...}" /> </i:Behaviors> 
    • Well, USB events look like a clean model. And it’s somehow not good to know about the view also: what if V doesn’t show right now, well, does the application live without USB messages? - VladD