views:

284

answers:

2

Hi,

I am a very novice WPF programmer, so need help in some simple tasks.

1- As the image shows, I want the (+) tab(it's a tab created with TabItem as of now or should it be button or something else? :-/) to create a tab left to it, say (A1), pressing again, should create (A2), sort of like in tabbed browsers......... And if possible, pressing (-) deletes the selected tab on user prompt.

alt text

2- I want, that user enters text in the textbox, with content separated by comma, or semicolon and that content is added to a COMBOBOX on Button press/click, I guess, it requires some sort of Databinding? Not sure,.. Such that, by the end of it, content from textbox, becomes list within ComboBox (on Button press) .................... And if possible, the text when selected in text box, on button click(same or different button which was chosen to add textbox content to combobox), it deletes the same from textbox and combobox .

Please help, I am learning WPF, but need this urgent help, which I cannot solve with my current knowledge of it. Thank you.

A: 

The tab control should be easy. Handle a Click event on the + tab, create a new tab and make it the active tab. Should be easy... I've gotta run so can't show you the code answer right now. Give it a try and get back with any issues.

The next part I can be a bit more helpful

<StackPanel>
            <TextBox x:Name="txtOptions" TextChanged="OnTextChanged"></TextBox>
            <ComboBox x:Name="cboSelect" ItemsSource="{Binding Path=Options}"/>
        </StackPanel>

the code-behind window (or your class) has to implement INotifyPropertyChanged. Also set the DataContext property of the window to itself (e.g. in the ctor ) like this.DataContext = this;

public partial class Window1 : Window, INotifyPropertyChanged
   {
       string[] _options;
       public string[] Options
       {
           get
           {  return _options;         }
           set
           {
               _options = value;
               if (this.PropertyChanged != null)
                   this.PropertyChanged(this, new PropertyChangedEventArgs("Options"));
           }
       }

The text changed event handler modifies the Options property on the code-behind and fires the notification that the property has been modified. The Combobox is notified since it is data bound to that property and self-updates.

private void OnTextChanged(object sender, TextChangedEventArgs e)
      {
          this.Options = txtOptions.Text.Split(';');
      }
Gishu
Tragically, the `TabItem` doesn't have a `Click` event. It doesn't even have a `Selected` event. It's up to the `TabControl` to respond when the user clicks on a tab. It also doesn't distinguish between clicking on a tab and navigating to the tab with the keyboard, which is unfortunate if you want those gestures to have different results, as you might in this case.
Robert Rossney
Hi Gishu, thanks for help.Though another issue arise, when I tried implementing and found that my ComboBox is not accessible due to reasons mentioned in my next question --> http://stackoverflow.com/questions/2302876/datagrid-editable-issue-access-issue-in-wpf .Please help. Thanks so much.
@Robert - Ah! Didn't know that - Geny, I think you'd have to read up on MVVM (Search for Josh Smith's article on that) to get any non-trivial stuff done easily in WPF
Gishu
+2  A: 

Everything's easier with data binding and MVVM. Harder at first, but ultimately lots easier.

Make two classes, Item and ItemCollection, that both implement INotifyPropertyChanged. Item should expose a string Text property, while ItemCollection should expose an ObservableCollection<Item> Items property and an Item SelectedItem property.

Make the ItemCollection class's constructor populate Items with test data and set SelectedItem.

This seems like a lot to do before you actually get around to implementing your tab control, but trust me, you'll like the result. Your XAML for the TabControl will look something like this:

<TabControl
    ItemsSource="{Binding Items}"
    SelectedItem="{Binding SelectedItem}">
    <TabControl.DataContext>
        <local:ItemsCollection/>
    </TabControl.DataContext>
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type local:Item}">
            <TextBlock Background="AliceBlue" Text="{Binding Text}"/>
        </DataTemplate>
    </TabControl.Resources>
    <TabControl.ItemContainerStyle>
        <Style TargetType="{x:Type TabItem}">
            <Style.Setters>
                <Setter Property="Header" Value="{Binding Text}"/>
            </Style.Setters>
        </Style>
    </TabControl.ItemContainerStyle>
</TabControl>

Let's understand what this does. It creates a TabControl. It creates an ItemsCollection object and sets it as the TabControl's DataContext. You've bound ItemSource to Items, so the TabControl will create a TabItem for each item. It will apply the ItemContainerStyle to each TabItem, which sets its Header property to the Item's Text property.

When the control renders the content of a tab, it finds the item it's rendering, searches through the resources to find a DataTemplate whose DataType matches the item, and uses that template. Since we've defined one in TabControl.Resources, you get a nice blue background and the Text property again.

This seems like a lot to go through. But now you don't have to write any code that manipulates the UI; you just write code that manipulates your ItemsCollection, and the UI pretty much takes care of itself.

Now let's take care of adding new tabs. What we're going to do is add a new item to the control that, when it becomes selected, adds a new item to the Items collection.

Create a new class, called, oh, ControlItem. Have it derive from Item. Modify your ItemsCollection constructor so that the last item it adds is a ControlItem, not an Item. And have it set the Text property on that item to "+".

Add this method to ItemsCollection:

public Item AddItem()
{
    Item newItem = new Item {Text = "New item"};
    Items.Insert(Items.Count-1, newItem);
    return newItem;
}

Now add to your window's code-behind, and as the SelectionChanged event handler for your TabControl:

void TabControl_SelectionChanged(object sender, RoutedEventArgs e)
{
    TabControl tc = (TabControl) sender;
    if (tc.SelectedItem is ControlItem)
    {
        ItemsCollection ic = (ItemsCollection) tc.DataContext;
        tc.SelectedItem = ic.AddItem();
    }
}

You can implement similar logic to remove an item from the list, but you'll need to introduce another variable to ItemsCollection to track the previously selected item, so that you can know which item to delete.

Another thing you can do: implement a Background property in Item and add a setter to the ItemContainerStyle that binds the TabItem's Background property to it. Then you can overload that property in ControlItem, so that your add and delete tabs look different.

You can also implement different subclasses for your control items, and have them expose a method that you call in the SelectionChanged event handler. That way, the event handler doesn't have to know anything about what the control item being clicked is doing. In fact, if you make the method part of Item, and have it do nothing unless it's overriden, the window doesn't even need to know that Item has subclasses.

That's the philosophy behind MVVM in a nutshell: Bind the view to objects about which it knows almost nothing. Let the view-model objects control what happens, so that the view doesn't have to.

Robert Rossney