2009-07-24 8 views
45

Tengo una vista que tiene un solo TextBox y un par Button s debajo. Cuando se carga la ventana, quiero que TextBox tenga foco.WPF MVVM Focus Field on Load

Si no estuviera usando MVVM, simplemente llamaría al TextBox.Focus() en el evento Loaded. Sin embargo, mi ViewModel no sabe acerca de mi vista, ¿cómo puedo lograr esto sin poner código en el código detrás de mi vista?

EDIT: Después de leer las respuestas que decidí poner este código en la vista XAML

<DockPanel FocusManager.FocusedElement="{Binding ElementName=MessageTextBox}">  
    <TextBox Name="MessageTextBox" Text="{Binding Message}"/> 
</DockPanel> 

Si esto fuera distinta de la página enfoque inicial que probablemente recomendaría la respuesta de Jon Galloway, ya que puede ser controlado de cualquier cosa el ViewModel.

Respuesta

44

Si te hace sentir mejor (me hace sentir mejor) se puede hacer esto en XAML utilizando una propiedad asociada:

http://msdn.microsoft.com/en-us/library/system.windows.input.focusmanager.focusedelement.aspx

Cualquier cosa que puede hacer en el código subyacente puede hacer en Xaml si conocer los trucos. Afortunadamente, no tienes que implementar este truco, MS lo hizo por ti.

+0

Eso es exactamente lo que estaba buscando. Creo que este código pertenece a la vista y, por alguna razón, me gusta más en el xaml que en el código subyacente. –

+2

La sugerencia de Galloway le permite controlar el enfoque desde ViewModel ... también puede examinarlo de cerca. –

+3

Después de leer algunas de las otras respuestas, no estoy seguro de que el código pertenezca a ViewModel. Parece estar directamente relacionado con la vista. Si tuviera una página más compleja que estuviera moviendo el foco en realidad, bastaría en otros eventos que probablemente tendrían sentido. Dicho esto, sigue siendo un buen truco para tener. –

14

En este caso, creo que está bien poner el código en la vista. Establecer el foco en un control afecta el comportamiento de la interfaz de usuario en lugar de la lógica de la aplicación y, por lo tanto, es responsabilidad de la vista.

+1

Eso es algo de lo que pensaba. Me siento un poco sucio cada vez que pienso en agregar código a la vista. En este caso, parece tener sentido, sin embargo. –

+14

Creo que una vez que cambié mi filosofía MVVM de "sin código detrás" a "código mínimo y pertinente detrás de" mi vida se hizo mucho más fácil. No había oído hablar de la propiedad adjunta que ha mencionado Anderson Imes, y parece una buena solución al mal gusto que le da el código detrás, pero no tema poner código * legítimo *, no lógico en el UI. –

+4

Estoy tan de acuerdo contigo. No creo que tenga sentido ser religioso sobre la eliminación del código de la vista. Lo importante es que el código en la vista debe estar muy relacionado con la interfaz de usuario. El código en la VM contiene el estado y otra lógica perteneciente al modelo. Una prueba de fuego es si el código podría/debería ser probado. El código en la vista NO se puede probar. –

4

Considero que el control con foco es mucho más que "solo visual", por lo que no tendría ningún problema con que haya código detrás.

La idea de una máquina virtual es alejar la lógica de la vista y proporcionar una versión amigable de enlace de datos de su modelo para que la vista se una. Esto no significa necesariamente que todos los códigos deberían residir en la máquina virtual, solo el código lógico y todo lo que no esté directamente relacionado con la interfaz de usuario.

8

En realidad, ¿no es el foco una preocupación de UI? MVVM trata de separar las preocupaciones: lo que pertenece al modelo está en el modelo, lo que pertenece a la vista está a la vista, y lo que une el modelo y la vista juntos está en ViewModel (esta es, por supuesto, una descripción simplificada).

Esto significa que la lógica de UI permanece en View - TextBox.Focus() es, en mi opinión, la forma adecuada de hacerlo realidad.

10
  1. Tiene una propiedad en su ViewModel que indica cuál es el elemento actualmente enfocado.
  2. Utilice el FocusManager para enlazar a esa propiedad.

    <Window FocusManager.FocusedElement="{Binding ElementName=ViewModel.FocusedItem}"/> 
    

Su modelo de vista es un traductor que existe únicamente para proporcionar información a la vista, lo que puede añadir cualquier información a la máquina virtual que el Vista necesita para funcionar.

+4

Pero luego tiene que nombrar todos sus elementos y usar ese nombre en la máquina virtual. Esto no funcionará si tiene una colección de elementos en la VM a la que se vincula la vista. – Carlos

+7

En mi humilde opinión, la VM no debería tener información sobre los nombres o tipos de control en la implementación de la vista real. En cambio, permitía que la vista escuchara una notificación de cambio de propiedad de la máquina virtual y luego dejara que la manejara. p.ej. ActivateNameField pasa a ser verdadero ... la Vista encuentra el control relevante y le asigna el foco. – Gishu

+1

Acepto que la VM debe tener un papel en el proceso cuando se trata de lógica empresarial. Una vez que el diseño exige "cuando seleccionen esta casilla de verificación y son administradores, entonces establezca el foco en el cuadro de texto OverrideReason", entonces la lógica pertenece a la máquina virtual. El truco es conseguir que la Vista respete el cambio de propiedad. – TheZenker

2

Después de tener un 'WPF Initial Focus Nightmare' y basado en algunas respuestas en la pila, la siguiente me resultó ser la mejor solución.

Primero, agregue su aplicación.xaml OnStartup() lo siguiente:

EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent, 
      new RoutedEventHandler(WindowLoaded)); 

A continuación, añadir el evento 'WindowLoaded también en App.xaml:

void WindowLoaded(object sender, RoutedEventArgs e) 
    { 
     var window = e.Source as Window; 
     System.Threading.Thread.Sleep(100); 
     window.Dispatcher.Invoke(
     new Action(() => 
     { 
      window.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); 

     })); 
    } 

La cuestión de roscado debe ser uso como WPF enfoque inicial sobre todo falla debido a algún tipo de marco condiciones de carrera.

He encontrado la siguiente solución mejor, ya que se utiliza globalmente para toda la aplicación.

creo que sirve ...

Oran