2009-06-05 6 views
13

Tengo un objeto que no puede heredar DependencyObject O uso NotifyPropertyChanged, y lo he unido a bastantes controles, por lo que cuando cambien las propiedades, no quiero ir a cada control y cambiar su valor en el código, así que estoy pensando que debe haber una manera de contar el XAML a "Volver a vincular" todo lo que es obligado a con una o dos líneas de código, en lugar de ir:WPF Force rebind

label1.Content = myObject.DontNotifyThis; 
label2.Content = myObject.DontNotifyThisEither; 
label3.Content = myObject.DontEvenThinkOfNotifyingThis; 
label4.Content = myObject.NotSoFastPal; 

así que en , etc ...

Este es un ejemplo muy simplificado:

XAML:

<Window x:Class="StackOverflowTests.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" x:Name="window1" Height="300" Width="300" Loaded="window1_Loaded"> 
    <Grid x:Name="gridMain"> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
     </Grid.RowDefinitions> 
     <Label Grid.Row="0" Content="{Binding Status}" ContentStringFormat="Today's weather: {0}" /> 
     <Label Grid.Row="2" Content="{Binding Temperature}" ContentStringFormat="Today's temperature: {0}" /> 
     <Label Grid.Row="1" Content="{Binding Humidity}" ContentStringFormat="Today's humidity: {0}" /> 
    </Grid> 
</Window> 

C#:

using System.Windows; 

namespace StackOverflowTests 
{ 
    /// <summary> 
    /// Interaction logic for Window1.xaml 
    /// </summary> 
    public partial class Window1 : Window 
    { 
     Weather weather = new Weather("Cloudy", "60F", "25%"); 

     public Window1() 
     { 
      InitializeComponent(); 
      this.DataContext = weather; 
     } 

     private void window1_Loaded(object sender, RoutedEventArgs e) 
     { 
      weather.Status = "Sunny"; 
      weather.Temperature = "80F"; 
      weather.Humidity = "3%"; 
     }  
    } 

    class Weather 
    { 
     public string Status { get; set; } 
     public string Temperature { get; set; } 
     public string Humidity { get; set; } 

     public Weather(string status, string temperature, string humidity) 
     { 
      this.Status = status; 
      this.Temperature = temperature; 
      this.Humidity = humidity; 
     } 
    } 
} 

he encontrado una manera de hacerlo, pero no es elegante en absoluto, y desafortunadamente, no puede simplemente establecer el DataContext a una nueva instancia de tiempo, se necesita ser la MISMA referencia (es por eso que lo puse en nulo por lo que cambia):

private void window1_Loaded(object sender, RoutedEventArgs e) 
{ 
    weather.Status = "Sunny"; 
    weather.Temperature = "80F"; 
    weather.Humidity = "3%"; 

    // bad way to do it 
    Weather w = (Weather)this.DataContext; 
    this.DataContext = null; 
    this.DataContext = w; 
} 

¡Gracias de antemano!

+0

curioso: ¿por qué ¿No puedes implementar INPC? –

+0

Estamos usando Deshacer/Rehacer en nuestra aplicación, INotifyPropertyChanging serializa el estado anterior del Objeto, INotifyPropertyChanged permite guardar el objeto en un nuevo archivo XmlSerialized. Sin embargo, estas propiedades específicas que necesito cambiar no alteran el estado de guardado del objeto (no cambia la fuente, el color, el fondo, el borde) ni nada que el usuario quiera guardar. Si NotifyPropertyChanging/Changed con esas propiedades, el sistema pensará que el objeto fue cambiado, pero para el usuario, no lo fue. Es por eso que no puedo usar eso. – Carlo

+0

Entendido, pero eso me suena como un diseño defectuoso. Sería mejor que INPC fuera una notificación de cambio de propiedad genérica, y luego otro mecanismo para rastrear los cambios de estado que le interesan deshacer/rehacer. Sin embargo, puede ser demasiado tarde para cambiar su diseño, por lo tanto, punto tomado. –

Respuesta

20

Si tiene acceso al elemento sobre el que desea actualizar el enlace, puede actualizar explícitamente el enlace. Puede recuperar la Expresión de enlace en el elemento y luego usar UpdateTarget() para actualizar la UI o UpdateSource para actualizar la propiedad de respaldo (si desea enlazar a algo editable como un TextBox).

Aquí está un ejemplo sencillo que muestra que:

<StackPanel> 
    <TextBlock x:Name="uiTextBlock" Text="{Binding MyString}" /> 
    <Button Click="Button_Click" 
      Content="Rebind" /> 
</StackPanel> 

public partial class Window1 : Window 
{ 
    public string MyString { get; set; } 

    public Window1() 
    { 
     MyString = "New Value"; 

     InitializeComponent(); 
     this.DataContext = this; 
    } 
    int count = 0; 
    private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     MyString = "Rebound " + ++count + " times"; 

     var bindingExpression = uiTextBlock.GetBindingExpression(TextBlock.TextProperty); 
     bindingExpression.UpdateTarget(); 
    } 
} 

(.. Yo recomendaría el uso de INotifyPropertyChanged aunque si es posible esa manera se puede extraer la lógica del código detrás)

+0

Ok, esto funciona, el único problema es, ¿tengo que hacerlo para cada control que necesito actualizar? – Carlo

+0

Si no puede implementar INotifyPropertyChanged, entonces sí. Puede crear una colección de BindingExpressions que necesita y luego usar la extensión linq. ForEach (b => b.UpdateTarget()). Sin embargo, si tiene que actualizar muchas cosas o solo tiene acceso a algunos de los controles a la vez, puede ser más simple 'ciclar' el DataContext como lo ha encontrado anteriormente. – rmoore

+0

Creo que actualizar cada control es factible, lo único que no me gusta de ese enfoque es que si agrego un nuevo control, tendré que agregarlo al código para actualizar también su objetivo. Pero no es un gran problema. ¡Gracias! – Carlo