views:

432

answers:

3

Hi,

I have a WPF TabControl which contains a number TabItems with child UserControls, like this.

XAML

    <TabControl x:Name="tabsMain"  HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" Grid.RowSpan="3" Background="lightgray">
                <TabItem Width="100" Header="Profile" FontSize="16">
                    <InfoControl:InfoTab x:Name="myInfo" />
                </TabItem>
                <TabItem Width="120" x:Name="Summary" Header="Summary"  FontSize="16">
                    <SummaryControl:SummaryTab/>
                </TabItem>
    </TabControl>

Within one of the UserControls, lets say, InfoTab, I have a button. When this button is clicked I would like to change the index of the TabControl to the SummaryTab and select a radio button on the SummaryTab page.

My problem is that the InfoTab user control does not have access to the MainUserControl which contains the TabControl shown above. I figured out a kludge which changes the "SelectedIndex" of the TabControl, but this is a very ugly solution and I'd prefer to do something more clean. Also I cannot currently change the RadioButton on my SummaryTab.

my Current C# hack:

  Private void btnSummaryDetails_Click(object sender, RoutedEventArgs e)
  {
    TabControl tabControl = UIHelper.FindChild<TabControl>(Application.Current.MainWindow, "tabsMain");
    tabControl.SelectedIndex = 7;
  }

Is it possible to use commands or dependency properties to select the SummaryTab and my desired radioButton? I'm still new to the WPF world, and would love to learn more about this. Thanks in advance.

See my post here for the UIHelper definition I use in the C# above.

+1  A: 

One thought comes to mind that will not require too many changes.

First, add an event to your InfoTab class:

public event EventHandler SummaryButtonClicked;

Then handle that in your main form by replacing the control declaration with:

<InfoControl:InfoTab x:Name="myInfo" SummaryButtonClicked="summaryButtonClicked" />

And give a name to your SummaryTab:

<SummaryControl:SummaryTab x:Name="summaryTab" />

Then add the event handler in your main form:

void MainWindow_SummaryButtonClicked(object sender, EventArgs e)
{
    this.summaryTab.SelectRadioButton();
}

And add a method in your SummaryTab class to select your radio button.

public void SelectRadioButton()
{
    // TODO: something like
    myRadioButton.IsChecked = true;
}
Jeremy
I currently implemented something very similar to what you suggested for my current solution! Thanks for your help, and see my post for what I ended up doing
CrimsonX
+1  A: 

You could probably use WPF routed events to solve your problem. Routed events use the WPF visual tree to send events up to parent controls (bubbling) or down to child controls (tunneling) without excessive coupling. I've tried to give a simple example below because I know that routed events can be a bit hairy to learn at first but it's well worth it...

In your main window, define a routed event and add a handler method:

public partial class MainWindow : Window {

    public static RoutedEvent ClickedEvent = EventManager.RegisterRoutedEvent(
        "Clicked",
        RoutingStrategy.Bubble,
        typeof(RoutedEventHandler),
        typeof(MainWindow));

    public MainWindow() {
        InitializeComponent();

        this.AddHandler(MainWindow.ClickedEvent, new RoutedEventHandler(OnClickedEvent));
    }

    public void OnClickedEvent(object sender, RoutedEventArgs e) {
        // do your work here

    }
}

In your button click handler, raise the event:

public partial class UserControl1 : UserControl {
    public UserControl1() {
        InitializeComponent();
    }

    private void button1_Click(object sender, RoutedEventArgs e) {

        // raise the event (gets bubbled up to the parent of the control)
        this.RaiseEvent(new RoutedEventArgs(MainWindow.ClickedEvent));

    }
}

The next step would be to tunnel another event down the visual tree and let the other usercontrol listen for it.

Andrew Jackson
I'm working on implementing this... thanks for the tips! I will follow up when I have more info/questions.
CrimsonX
A: 

I ended up adding a public method as Jeremy suggested in his post. A simple but effective solution. Thanks Jeremy!

Another key realization was that in order to switch the tabcontrol by index, I can get a reference to the main user control and set the SelectedItem to the TabItem itself, like this:

  Private void btnSummaryDetails_Click(object sender, RoutedEventArgs e)  
 {

    //HACK replace this with a command that toggles to a different tab instead of this tab reference
    MainUserControl mainUserControl = UIHelper.FindChild<MainUserControl>(Application.Current.MainWindow, "root");
    mainUserControl.tabsMain.SelectedItem = mainUserControl.Summary;

    mainUserControl.SummaryUserControl.SelectRadioButton();
  }

Then as suggested by Jeremy, my solution was something like:

public void SelectRadioButton()
{
    // TODO: something like
    myRadioButton.IsChecked = true;
}

My XAML structure was like

// my main user control: 
<UserControl 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
x:Name="root"
>
<TabControl x:Name="tabsMain" ...> 
<TabItem x:Name="Summary" ... />
</TabControl>
</UserControl>

I think Andrew Jackson's comments are absolutely valid - long term, I plan to investigate using routed command or routed events to traverse the visual tree, but for now I'm sticking with this "quick and dirty" solution as we're not shipping this product. Based on my investigation Routed Events might be a little overkill for this situation.

CrimsonX