2010-02-11 17 views
12

Tengo un TextBox con un ContextMenu en él. Cuando el usuario hace clic derecho dentro del TextBox y elige el MenuItem apropiado, me gustaría tomar el SelectedText en mi viewmodel. No he encontrado una buena manera de hacer esto de la manera "MVVM".MVVM y la propiedad TextoTexto de TextBox

Hasta ahora tengo mi aplicación utilizando la forma de MVVM de Josh Smith. Estoy buscando transferirme a Cinch. No estoy seguro si el marco de Cinch manejará problemas como este. ¿Pensamientos?

Respuesta

18

No hay una forma directa de enlazar SelectedText a una fuente de datos, porque no es una DependencyProperty ... sin embargo, es bastante fácil crear una propiedad adjunta que podría enlazar.

Aquí es una implementación básica:

public static class TextBoxHelper 
{ 

    public static string GetSelectedText(DependencyObject obj) 
    { 
     return (string)obj.GetValue(SelectedTextProperty); 
    } 

    public static void SetSelectedText(DependencyObject obj, string value) 
    { 
     obj.SetValue(SelectedTextProperty, value); 
    } 

    // Using a DependencyProperty as the backing store for SelectedText. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty SelectedTextProperty = 
     DependencyProperty.RegisterAttached(
      "SelectedText", 
      typeof(string), 
      typeof(TextBoxHelper), 
      new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedTextChanged)); 

    private static void SelectedTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
    { 
     TextBox tb = obj as TextBox; 
     if (tb != null) 
     { 
      if (e.OldValue == null && e.NewValue != null) 
      { 
       tb.SelectionChanged += tb_SelectionChanged; 
      } 
      else if (e.OldValue != null && e.NewValue == null) 
      { 
       tb.SelectionChanged -= tb_SelectionChanged; 
      } 

      string newValue = e.NewValue as string; 

      if (newValue != null && newValue != tb.SelectedText) 
      { 
       tb.SelectedText = newValue as string; 
      } 
     } 
    } 

    static void tb_SelectionChanged(object sender, RoutedEventArgs e) 
    { 
     TextBox tb = sender as TextBox; 
     if (tb != null) 
     { 
      SetSelectedText(tb, tb.SelectedText); 
     } 
    } 

} 

entonces Se puede utilizar como esa en XAML:

<TextBox Text="{Binding Message}" u:TextBoxHelper.SelectedText="{Binding SelectedText}" /> 
+0

Gracias! Esto hizo el truco. Tan obvio y me lo perdí. Gracias de nuevo. – Eric

+0

Estoy tratando de hacer lo mismo para la propiedad CaretIndex, pero parece que no está funcionando. Puede ayudar – TheITGuy

+0

@TheITGuy, no sin ver su código ... Probablemente debería crear una nueva pregunta (puede publicar el enlace aquí, le responderé si puedo) –

2

Las aplicaciones de ejemplo en la WPF Application Framework (WAF) eligieron otra forma de resolver este problema. Allí, ViewModel puede acceder a la Vista a través de una interfaz (IView) y, por lo tanto, puede solicitar el Texto Seleccionado actual.

Creo que no se debe usar el enlace en todos los escenarios. A veces escribir algunas líneas en el código subyacente es mucho más limpio que usar clases de ayuda altamente avanzadas. Pero eso es sólo mi opinión :-)

jbe

+0

Esta solución tiene la ventaja de poder insertar el valor del texto seleccionado en cualquier propiedad de cadena con un setter público. La flexibilidad puede decirse que supera las líneas adicionales de código. Además, con algunos pequeños retoques, la solución se podría usar para unir las propiedades SelectionStart y SelectionEnd, permitiendo que los modelos de vista se configuren fácilmente y reciban la selección de texto. – Gusdor

1

Sé que ha sido contestada y aceptada, pero pensé que me gustaría añadir mi solución. Uso un Comportamiento para establecer un puente entre el modelo de vista y el TextBox. El comportamiento tiene una propiedad de dependencia (CaretPositionProperty) que se puede vincular en dos sentidos al modelo de vista. Internamente, el comportamiento trata con las actualizaciones hacia/desde el TextBox.

public class SetCaretIndexBehavior : Behavior<TextBox> 
    { 
     public static readonly DependencyProperty CaretPositionProperty; 
     private bool _internalChange; 

    static SetCaretIndexBehavior() 
    { 

    CaretPositionProperty = DependencyProperty.Register("CaretPosition", typeof(int), typeof(SetCaretIndexBehavior), new PropertyMetadata(0, OnCaretPositionChanged)); 
} 

public int CaretPosition 
{ 
    get { return Convert.ToInt32(GetValue(CaretPositionProperty)); } 
    set { SetValue(CaretPositionProperty, value); } 
} 

protected override void OnAttached() 
{ 
    base.OnAttached(); 
    AssociatedObject.KeyUp += OnKeyUp; 
} 

private static void OnCaretPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{ 
    var behavior = (SetCaretIndexBehavior)d; 
    if (!behavior._internalChange) 
    { 
     behavior.AssociatedObject.CaretIndex = Convert.ToInt32(e.NewValue); 
    } 
} 

    private void OnKeyUp(object sender, KeyEventArgs e) 
    { 
     _internalChange = true; 
     CaretPosition = AssociatedObject.CaretIndex; 
     _internalChange = false; 
    } 
} 
Cuestiones relacionadas