tags:

views:

118

answers:

4

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;
    }
}
A: 

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.

Preet Sangha
Erm, I don't get it. How will a `ValueConverter` help me let my ViewModel(/Commands actually) know when to enable/disable controls? I thought that is for erm converting values say "1" to "One"
jiewmeng
A: 

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.

TerrorAustralis
yes. this is the way I do it. But I think you misunderstood my question. This displays my `ViewModels`. I want to enable/disable buttons based on the type of `ViewModel` displayed and maybe attributes of controls within the `View`
jiewmeng
Binding between controls is simple, just use `{Binding ElementName=otherControl, Path=otherControlProperty}`. And if you have two different views defined as above, then they should handle different types of ViewModels, so you could just set the buttons to enabled or not enabled on the different data templates
TerrorAustralis
I roughly get the idea but can you give a fuller example? I am still abit lost :)
jiewmeng
Edits have been made. Try it out
TerrorAustralis
A: 

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.

Carles
`GetActiveWorkspace()` is your own method? I guess it will somehow return what ViewModel is displayed or something?
jiewmeng
Yes, it returns the active view model. I think I use an ICollectionView to get the currentitem of the collection.
Carles
+2  A: 

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" />
Steve Greatrex
+1 I think this is the way to do it.
Carles
Hey, thanks for this answer, very helpful. I am using MVVM Foundation and `RelayCommand` how do i raise `CanExecuteChange` from inside `CurrentChanged`. see update
jiewmeng
I'm not familiar with the MVVM Foundation framework myself, but from the looks of the source for RelayCommand it would not be possible without extending it. If the only scenario you want the 'enabled' value to change is when the selected tab changes then you can avoid using the RelayCommand as you don't need the hook up to the CommandManager.RequerySelected event that it uses (and is the only real difference between it and what I have written above)
Steve Greatrex
nvm i solved it! update 2
jiewmeng