Nick
Nick

Reputation: 9183

Add Composite WPF Region at runtime

Ok, here's my simple scenario. I've got collection of strings that I'm binding to a TabControl as a proof of concept. As I add strings I want a new tab with the region name as the header and a ItemsControl in the Tab container. That ItemsControl should define a new region.

        <TabControl  x:Name="tabDemo" ItemsSource="{Binding DynamicRegions}" >
        <TabControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate>
                <ItemsControl cal:RegionManager.RegionName="{Binding}" ItemsSource="{x:Null}">

                </ItemsControl>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>

From there I add strings to the collection. The tabs show up fine, but when I try to call

   private void AddDynamicRegion(object arg)
  {
     var newRegionName = "Region" + (DynamicRegions.Count + 1).ToString();
     DynamicRegions.Add(newRegionName);
  }

  private void AddRandomRegionContent(object arg)
  {
     if (string.IsNullOrEmpty(SelectedRegion) )
        return; 

     Debug.WriteLine("Injected "  + RegionContent + " into " + SelectedRegion);

     var newContent = new TextBlock() { Text = RegionContent };
     _regionManager.RegisterViewWithRegion(SelectedRegion,() => newContent );

     _regionManager.Regions[SelectedRegion].Activate(newContent);
  }

It either throws an exception that the region doesn't exist or an exception that creating the region failed and my ItemsControl.ItemsSource is already set. I didn't really expect this to work out of the box, but is there any way I can create dynamic regions and inject into them at run time?

Update: Calling RegisterViewWithRegion actually injects my textblock...but getting some weird behavior between tabs.

I changed it so I can choose the region and text I want to inject. It always works for the first region I create, but after that, flipping between tabs just shows the stuff I've added to the first region. Is the tab control re-using my datatemplate across multiple tabs? I've included all my code from the ViewModel. DynamicRegions is just an ObservableCollection

Upvotes: 0

Views: 5949

Answers (3)

Jeremiah Morrill
Jeremiah Morrill

Reputation: 4268

Would making the TabControl a region work for you? That way you can just add regions to that view. If those views need a region, just create a sub/nested region and view inject to that also. I really have never needed to dynamically create regions...

     <TabControl cal:RegionManager.RegionName="TabRegion" x:Name="tabDemo" ItemsSource="{Binding Something}" >
        <TabControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
     </TabControl>

The StockChartsRI in Prism show how you can bind the tab's header right from your view model. They use a subclass of the TabPanel (AnimatedTabPanel), but the setup is the same.

Upvotes: 1

Nick
Nick

Reputation: 9183

Ok, I've got something that works, but I'd like to see what Anderson Imes comes up with.

Basically, I set all the region stuff in code when my tabItems are generated using databinding.

  private void AddDynamicRegion(object arg)
  {
     var newRegionName = "Region" + (DynamicRegions.Count + 1).ToString();
     DynamicRegions.Add(newRegionName);

     var tabItem = View.tabDemo.ItemContainerGenerator.ContainerFromIndex(DynamicRegions.Count - 1) as TabItem;
     var newRegionContainer = new ItemsControl();
     RegionManager.SetRegionName(newRegionContainer,newRegionName);
     RegionManager.SetRegionManager(newRegionContainer, _regionManager);
     tabItem.Content = newRegionContainer;


  }

So then in insert I can just do this, and everything shows up under the correct region. I wonder if there is a better way to do it though.

  private void AddRandomRegionContent(object arg)
  {

     if (string.IsNullOrEmpty(SelectedRegion) )
        return; 

     Debug.WriteLine("Injected "  + RegionContent + " into " + SelectedRegion);

     var newContent = new TextBlock() { Text = RegionContent };
     var region = _regionManager.Regions[SelectedRegion];
     if (region == null)
     {
        Debug.WriteLine("Couldn't find region");
        return;
     }

     region.Add(newContent);
     region.Activate(newContent);

  }

Upvotes: 0

Anderson Imes
Anderson Imes

Reputation: 25650

You need to call region.Activate(stuffIJustAddedToTabsControl) on at least one of the things you add to the region. I'll get the tab control and the region in sync. Otherwise it looks all crazy and acts worse.

Upvotes: 2

Related Questions