简体   繁体   中英

How to bind usercontrols in xaml?

I have MainWindow containing a datagrid and a "filter panel". The filter panel can change by a user input(button click). I try to achieve it with databinding. The problem that Im facing is the filter panel(which is a user control) is not loaded or refreshed.

Mainwindow xaml:

 <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="250*" />
        <ColumnDefinition Width="253*" />
    </Grid.ColumnDefinitions>
    <DataGrid AutoGenerateColumns="True" Height="200" HorizontalAlignment="Left" Margin="23,28,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="200" ItemsSource="{Binding OverviewableItems}" /> 
    <UserControl Grid.Column="1" Content="{Binding UserControl}" DataContext="{Binding}" Grid.ColumnSpan="2" />

    <Button Content="PersonFilter" Height="23" HorizontalAlignment="Left" Margin="23,268,0,0" Name="buttonPersonFilter" VerticalAlignment="Top" Width="75" Click="buttonPersonFilter_Click" />
    <Button Content="ProjectFilter" Height="23" HorizontalAlignment="Left" Margin="132,268,0,0" Name="buttonProjectFilter" VerticalAlignment="Top" Width="75" Click="buttonProjectFilter_Click" />

</Grid>

code behind:

 private ViewModel _viewModel;
    public MainWindow()
    {
        _viewModel = new ViewModel(new DataProvider());
        DataContext = _viewModel;
        _viewModel.PropertyChanged += _viewModel.SetFilterType;
        InitializeComponent();
    }


    private void buttonProjectFilter_Click(object sender, RoutedEventArgs e)
    {
        _viewModel.OverviewType = OverviewType.Project;
    }

    private void buttonPersonFilter_Click(object sender, RoutedEventArgs e)
    {
        _viewModel.OverviewType = OverviewType.Person;
    }

First user control:

<Grid>
    <DatePicker Grid.Column="1" Grid.Row="1" Height="25" HorizontalAlignment="Left" Margin="19,18,0,0" Name="datePickerFundingTo" VerticalAlignment="Top" Width="115" Text="{Binding ElementName=ProjectFilter, Path=FundingTo}" />
</Grid>

code behind for this user control is only this:

 public DateTime FundingTo { get; set; }
 public ProjectFilter()
    {
        FundingTo = DateTime.Now;

        InitializeComponent();
    }

Other user control: just simply contains a TextBox and a Button, for the sake of simplicity I didnt add any code behind to it.

ViewModel of the MainWindow:

public class ViewModel  : INotifyPropertyChanged
{
    private UserControl _userControl;

    public UserControl UserControl
    {
        get { return _userControl; }
        set
        {
            if (_userControl == value)
            {
                return;
            }
            OnPropertyChanged("UserControl");
            _userControl = value;
        }
    }

    private OverviewType _overviewType = OverviewType.None;
    public OverviewType OverviewType
    {
        get { return _overviewType; }
        set
        {
            if (_overviewType == value)
            {
                return;
            }
            OnPropertyChanged("OverviewType");
            _overviewType = value;
        }
    }

    private ObservableCollection<IOverviewItem> _overviewableItems;
    public ObservableCollection<IOverviewItem> OverviewableItems
    {
        get { return _overviewableItems; }
        set
        {
            if (_overviewableItems == value)
            {
                return;
            }
            _overviewableItems = value;
        }
    }

    private readonly DataProvider _dataProvider;

    public event PropertyChangedEventHandler  PropertyChanged;

    public ViewModel(DataProvider dataProvider)
    {
        _dataProvider = dataProvider;
    }

    public void SetFilterType(object sender, EventArgs eventArgs)
    {

        switch (_overviewType)
        {
            case OverviewType.Project:
                _userControl = new ProjectFilter();
                break;
            case OverviewType.Person:
                _userControl = new PersonFilter();
                break;
        }
    }

    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged == null)
            return;

        var eventArgs = new PropertyChangedEventArgs(name);
        PropertyChanged(this, eventArgs);
    }  
}

plus I have an enum OverviewType with None,Project,Person values.

The property changed event fired properly, but the user control is not refreshed. Could anyone enlight me, where is the flaw in my solution?

And the other question I have, how can I communicate from the usercontrols to the mainwindow viewmodel? Forex: the datagrid should be changed according to its filter.

Any help would be greatly appreciated. Cheers!

Try to fire the PropertyChanged after changing a property's backing field:

public UserControl UserControl
{
     get { return _userControl; }
     set
     {
         if (_userControl != value) 
         {
            _userControl = value;              // first
             OnPropertyChanged("UserControl"); // second
         }
     } 
}

Similar for OverviewType .

There are different problems here.

  • As Clemens said, you must fire your event after the value is updated. But it's not the main issue here.

  • Second problem: you are affecting your new usercontrol to the private member , so you're totally bypassing your property.

Replace

_userControl = new ProjectFilter();

by

this.UserControl = new ProjectFilter();
  • Third problem, which is not directly related to your question but actually is your biggest problem : you have an architecture design issue . You're exposing in your viewmodel a UserControl , which is an anti-pattern . Your viewmodel must not know anything about the view, so it must NOT have any reference to the controls inside the view. Instead of the binding you wrote, you could fire an event from the viewmodel and add an event handler in your view so it's your view that updates the usercontrol.

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