简体   繁体   中英

C#/MVVM: Enable/Disable Buttons based on another control's property

Say i have a tab control that displays data of various types, eg EditorTabViewModel , PreviewTabViewModel both inheriting from TabViewModel . The implementation is similar to the tutorial on MSDN

I want to enable buttons depending on the active tab, whether its an EditorTabViewModel or a PreviewTabViewModel . How can I achieve this?

UPDATE

public ICommand EditorCommand
{
    get
    {
        if (_editorCommand == null) {
            _editorCommand = new RelayCommand(() =>
            {
                MessageBox.Show("Editor");
            }, () =>
            {
                var enabled = true;
                var viewSource = CollectionViewSource.GetDefaultView(Tabs);
                viewSource.CurrentChanged += (o, e) =>
                {
                    if (viewSource.CurrentItem is EditorTabViewModel)
                    {
                        enabled = false;
                    }
                };
                return enabled;
            });
        }
        return _editorCommand;
    }
}

UPDATE 2

public ICommand PreviewCommand
{
    get
    {
        if (_previewCommand == null) {
            _previewCommand = new RelayCommand(() =>
            {
                MessageBox.Show("Preview");
            }, () =>
            {
                var viewSource = CollectionViewSource.GetDefaultView(Tabs);
                var enabled = viewSource.CurrentItem is EditorTabViewModel;
                viewSource.CurrentChanged += (o, e) =>
                {
                    CommandManager.InvalidateRequerySuggested();
                };
                return enabled;
            });
        }
        return _previewCommand;
    }
}

I would suggest that you create an ICommand implementation that constructs on the ICollectionView that contains the 2 tab controls. The command can then react to the CurrentChanged event from the collection view to determine whether or not it should be enabled, and raise a CanExecuteChanged event to indicate a change.

class MyCommand : ICommand
{
    private bool _isEnabled = true;

    public MyCommand(MyTopLevelViewModel viewModel)
    {
        var viewSource = CollectionViewSource.GetDefaultView(viewModel.Tabs);
        viewSource.CurrentChanged += (o,e) =>
            {
                _isEnabled = (viewSource.CurrentItem is EditorTabViewModel); //or however you want to decide

                if (this.CanExecuteChanged != null) 
                     this.CanExecuteChanged(this, EventArgs.Empty);

            };
    }

    public void Execute(object parameter) { /*...*/ }

    public bool CanExecute(object parameter) { return _isEnabled; }

    public event EventHandler CanExecuteChanged;
}

Note: you will need to set the IsSyncronizedWithCurrentItem property on the tab control:

<TabControl IsSynchronizedWithCurrentItem="True" />

I would use a ValueConverter and pass the Active one as the value

You can convert from anything to anything. Passing in Active form can determine the type, and thus return true/false for binding to the buttons enabled property.

It may be more in line with MVVM to do 2 Views (data templates). Just do them as generics in the control resources.

<TabControl>
<TabControl.Resources>
    <DataTemplate DataType="EditorTabViewModel">
         <Button Content="Enabled for Editor only" IsEnabled=True Command=SomeCommand />
         <Button Content="Enabled for Preview only" IsEnabled=False Command=SomeCommand />
    </DataTemplate/>
    <DataTemplate DataType="PreviewTabViewModel">
        <Button Content="Enabled for Editor only" IsEnabled=False Command=SomeCommand />
        <Button Content="Enabled for Preview only" IsEnabled=True Command=SomeCommand />
    </DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
    <DataTemplate DataType="TabViewModel">
        <TextBlock Text="{Binding SomeValueToShowAsHeader}" />
    </DataTemplate>
</TabControl.ItemTemplate>
</TabControl>

So what happens is all the tabs have the same tab header, because the itemtemplate is explicitly defined. But because the ContentTemplates are not explicitly defined, when the control looks for a template for the content, it will give it the default template for the domain. Since we define 2 default templates, one for EditorTabViewModel and one for PreviewTabViewModel, when it comes across one of these, it will assign it to that data template. This should result in two different displays showing in the tab collection. I havent tried it fully with tab viewmodel so let me know if it doesnt work as expected.

Add a readonly property on your window view model that represents the visibility of the tabs:

public bool EditorTabVisible{
    get{
        return GetActiveWorkspace() is EditorTabViewModel;
    }
}

You need to also have an event thet fires when you change the active tab and add this to the event handler:

OnPropertyChanged("EditorTabVisible");

Then you can bind the IsEnabled property of the button to this property.

I don't know if there's a better way to do it, but this works for me.

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