简体   繁体   中英

Difficulties with data-binding

Databinding is a difficult concept that I still can't quite grasp despite reading through dozens of topics already.

I want to have a TextBox that would change its text every time a field 'status' is being changed in my code-behind, for debugging purposes.

Here's what I have so far:

public partial class ReviewsControl : UserControl
{
    private Status status = MainControl.AppStatus;
    public string StatusDisplay
    {
        get
        {
            return status.ToString();
        }
    }
}

And here's my take on the view:

<TextBlock x:Name="StatusBlock" HorizontalAlignment="Left" Margin="10,450,0,0" TextWrapping="Wrap" Text="{Binding StatusDisplay, Source=ReviewsControl, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Height="40" Width="205"/>

The code above doesn't even show anything, let alone do that dynamically. What should I change if I want XAML to detect changes in the in my C# code and change the textbox accordingly?

I too had issues early on. Your view (display to end-users) does not care how or where things come from, you just know what will be exposed in your View Model controller to bind to. To have things updated in your view, the most common is binding and having your view model include the INotifyPropertyChanged interface. This is so you can force raising the event when a property changes, whatever is "listening" for it will update itself..

Simple class, you can just grab from the : INotify, the event exposed to allow things to get registered to, and your method to actually RAISE the event to pass up stream to those listening for changes.

public class SomeBaseClass : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;
   public void RaisePropertyChanged(string propertyName)
   {
      if (PropertyChanged != null)
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
   }
}

Once that is done, your going to expose properties in your class by making as public with getter/setters. You don't bind to a field.

public string SomeThingYouWantToExpose {get; set; }

And in your code, however you are getting data, label refresh, whatever, you would set and raise the property-changed

public void GettingSomeData()
{
   // … doing something to get / prepare / whatever...
   SomeThingYouWantToExpose = "some new label";
   // Now raise which the view bound to this property will updated itself
   RaisePropertyChanged( "SomeThingYouWantToExpose" );
}

Now, in your view, you would bind to whatever your view model object is and then the property on the class. Don't know if you specifically need the x:Name reference which basically makes this a field in your control. Not necessary in this example unless you are trying to bind other controls in same display as a result of this field..

<TextBlock Height="40" Width="205"  Margin="10,450,0,0"
   HorizontalAlignment="Left" VerticalAlignment="Top" 
   DataContext="{Binding YourViewModelObjectName}"
   Text="{Binding SomeThingYouWantToExpose}" 
   TextWrapping="Wrap" />

Hopefully these pieces within your scenario will make sense and move you forward in your project. Any additional clarification, let me know.

CLARIFICATION on the DATA CONTEXT BINDING

The way I have implemented in my apps, I would have a

MyView -- via the visual declaration... be it a window, grid, complex user control with many controls, whatever...


MyDataModel - a class that is used to query data from whatever data source, such as SQL-Server.


MyView_ViewModel - a custom class that has the INotifyPropertyChanged incorporated and where I expose different properties and other objects I want to expose / make available to the view

So, in the MyData_ViewModel, I would create the view and also create my view model. After creating the view, I would set the overall view's DataContext to the "MyView_ViewModel"

public class MyData_ViewModel : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;
   public void RaisePropertyChanged(string propertyName)
   {
      if (PropertyChanged != null)
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
   }

   public void LoadingMyDataAndView()
   {
      // Controller I use to get whatever data I need from ex: SQL
      _myDataModel = new MyDataModel();

      // the view / window / control I want to present to users
      _myView = new MyView();

      // Now, set the data context on the view to THIS ENTIRE OBJECT.
      // Now, anything on THIS class made as public can be have a binding
      // directly to the control in the view.  Since the DataContext is 
      // set here, I can bind to anything at this level or lower that is public.
      _myView.DataContext = this;
   }

   private MyView _myView;
   private MyDataModel _myDataModel;

   // such as example exposed property
   public string SomeThingYouWantToExpose {get; set; }

   public void GettingSomeData()
   {
      var something = _myDataModel.GetSomeData();

      // … doing something to get / prepare / whatever...
      SomeThingYouWantToExpose = "some new label";
      // Now raise which the view bound to this property will updated itself
      RaisePropertyChanged( "SomeThingYouWantToExpose" );
   }
}

Hopefully this EXAMPLE shows how the pieces tie together. The view would no longer need the individual DataContext set since the whole view is set, just needs to bind to the individual public property.

Assuming that the TextBlock is a child element of the UserControl, ie that

<TextBlock x:Name="StatusBlock" Text="{Binding StatusDisplay}" ... />

is declared in the UserControl's XAML, the Binding's RelativeSource should be set to the parent UserControl like this:

<TextBlock Text="{Binding StatusDisplay,
                  RelativeSource={RelativeSource AncestorType=UserControl}}" />

Since StatusDisplay is a property of a UserControl, ie a DependencyObject, it should be declared as a dependency property:

public partial class ReviewsControl : UserControl
{
    public static readonly DependencyProperty StatusDisplayProperty =
        DependencyProperty.Register(
            nameof(StatusDisplay), typeof(string), typeof(ReviewsControl);

    public string StatusDisplay
    {
        get { return (string)GetValue(StatusDisplayProperty); }
        set { SetValue(StatusDisplayProperty, value); }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM