How to change TabItem indices of a TabControl at runtime? - wpf

I have five TabItem's in my TabControl and I need to move the position of each tab continuously at runtime. Can anyone tell me how can I change tab index from one position to another position at runtime.
Thanks,
#nagaraju.

Use the below solution:
TabItem tempTab = new TabItem();
tempTab = control.Items[0] as TabItem;
control.Items[0] = control.Items[1];
control.Items[1] = tempTab;
This will definitely work and you have to do from code behind.

If you are using ObservableCollection the you Just have to change the position of the Item in your collection it will be refelected in View...
For Example..
<TabControl ContentTemplate="{StaticResource ResourceKey=listView}"
ItemContainerStyle="{StaticResource ResourceKey=myTabItem}"
ItemsSource="{Binding Path=Persons}"
SelectedItem="{Binding Path=SelectedPerson}"
Style="{StaticResource ResourceKey=myTab}"
TabStripPlacement="Bottom">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="16"
Height="16"
Margin="0,0,2,0"
Source="Themes\Water lilies.jpg" />
<TextBlock Margin="0,4,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
<Button Grid.Row="1" Width="50" Command="{Binding Path=ChangePositionCommand}"> ClickMe </Button>
Here you just Change the Position of the item in TabList in ViewModel and that Position will be changed accordingly...
In Your ViewModel
I have the implementation if getting Data and Setting Up Commands... that up to you how you Do it
public ICommand ChangePositionCommand { get; private set; }
public Person SelectedPerson
{
get { return selectedPerson; }
set
{
selectedPerson = value;
InvokePropertyChanged(new PropertyChangedEventArgs("SelectedPerson"));
}
}
private void ChangePosition(object obj)
{
int index = Persons.IndexOf(SelectedPerson);
if (index <= (Persons.Count-1))
{
Persons.Move(index,index+1);
}
else
{
Persons.Move(index,0);
}
}
The above code my give INdex out of bound but I am no where near an IDE so cant test that you could reapir it according to you.

You need to change the TabControl.Items Collection. Remove the tab from the old Position and set it on a new Positon.
See How to change the order of the TabItem in the wpf TabControl

Related

WPF - Delete selected item (BitmapImage) from listview

I have a Listview and for it's ItemsSource I have set CollectionOfCapturedImages (an ObservableCollection) ,an a Button for deleting selected items (BitmapImage) from Listview and also from ObservableCollection and a Label in my MainWindow displaying amount of captured images.
private void addNewImageButton_Click(object sender, RoutedEventArgs e)
{
CameraWindow cWindow = Application.Current.Windows.OfType<CameraWindow>().FirstOrDefault();
RoutedEventArgs newEventArgs = new RoutedEventArgs(Button.ClickEvent);
cWindow.manualCapture.RaiseEvent(newEventArgs);
// ListView.ScrollIntoView(ListView.Items.Count - 1);
}
public ObservableCollection<BitmapImage> CollectionOfCapturedImages { get; } = new ObservableCollection<BitmapImage>();
<ListView x:Name="ListView" ItemsSource="{Binding CollectionOfCapturedImages}" Height="345" Margin="567,10,10,0" VerticalAlignment="Top">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" HorizontalAlignment="Center" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Image Source="{Binding}" Height="150" Width="150"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
this is how I'm updating my Label in MainWindow. When I'm capturing images there is no problem, Label's content is getting updated.
public Action<int> amountOfCapturedImages;
this.cameraWindow = new CameraWindow(this);
cameraWindow.amountOfCapturedImages += (int count) => {
MwAmountOfImagesLabel.Content = count;
};
and here is my delete button
<Button x:Name="DeleteSelectedImageButton" Click="DeleteSelectedImageButton_Click" Content="Delete Selected Image" HorizontalAlignment="Left" Margin="567,488,0,0" Height="26" VerticalAlignment="Top" Width="145"/>
and this way I'm deleting BitmapImages from Listview
private void DeleteSelectedImageButton_Click(object sender, RoutedEventArgs e)
{
CollectionOfCapturedImages.Remove((BitmapImage)ListView.SelectedItem);
}
but my Label's content is not getting updated when I delete an image from Listview. How could I correctly update my Label's content?
You must remove them from the CollectionOfCapturedImages and bind MwAmountOfImagesLabel => CollectionOfCapturedImages.Count
Get rid of this code:
cameraWindow.amountOfCapturedImages += (int count) => {
MwAmountOfImagesLabel.Content = count;
};
...and simply bind the Content property of the Label the Count property of the ObservableCollection:
<Label Content="{Binding CollectionOfCapturedImages.Count}" />
You should not set the Content property of the Label programmatically somewhere in your code. Just set up the binding in your XAML markup.

ListBox with WrapPanel - Remove item / arrow key navigation

I have a strange problem regarding my listbox / wrappanel arrow navigation.
My Listbox :
<ListBox x:Name="List"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="{Binding MainIndex, Mode=OneWayToSource}"
SelectedItem="{Binding CurrentFeed, Mode=TwoWay}"
ItemsSource="{Binding CurrentFeedList}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" MaxWidth="{Binding ActualWidth, ElementName=Panel}" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Width="168">
<Border BorderBrush="White" BorderThickness="0" Margin="0,7,0,0">
<Image Source="{Binding Image , Converter={StaticResource ByteArraytoImageSource}}" Width="150" Height="213" ></Image>
</Border>
<Border BorderBrush="White" BorderThickness="0" Height="65" HorizontalAlignment="Center" >
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap" FontFamily="{StaticResource DefaultFontFamily}" FontSize="15" Text="{Binding Title}" Foreground="White" Cursor="Hand" HorizontalAlignment="Center" TextAlignment="Center"/>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
The render :
(source: free.fr)
The render is great No problem ! ;)
As you can see it's a list of wallpaper retrieved from RSS.
I have bind a shortcut to make one item readed and then it's removed from the ObservableCollection :
<UserControl.InputBindings>
<KeyBinding Key="D" Modifiers="Control" Command="{Binding ReadCmd}" />
</UserControl.InputBindings>
In My View Model :
ObservableCollection<Feed> CurrentFeedList { get; set; }
private Feed _currentFeed;
public Feed CurrentFeed
{
get { return _currentFeed; }
set
{
_currentFeed = value;
OnPropertyChanged("CurrentFeed");
}
}
public ICommand ReadCmd { get; set; }
public int MainIndex { get; set; }
My ReadCmd is a RelayCommand that call "ReadAction" method.
At the beginning I simply remove the item from the ObservableCollection, but I want to type Ctrl+D twice to read 2 item.
So I decided to get the index of the listbox and select back the same index after removing one.
private void ReadAction()
{
int previousIndex = MainIndex;
if (previousIndex == CurrentFeedList.Count - 1)
previousIndex -= 1;
CurrentFeedList.Remove(CurrentFeed);
CurrentFeed = CurrentFeedList[previousIndex];
}
It's work as I wanted, but one problem remain and I can't solve it.
When I push Ctrl+D the SelectedItem is remove and the next item become Selected. But then when I use the arrow key navigation to navigate over the list, it jump each time to the first item of the list.
I hope it's enough clear.
Thanks you.
This issue is due to the fact that when you are removing the items using the KeyBinding, ListBox loses the focus and hence pressing the arrow keys after that places focus on first item of the ListBox.
So in order to make your arrow keys work from the selected item you will also have to set the focus on Selected Item of the Listbox.
There are couple of ways of doing this, e.g using attached behavior to set the focus on the ListBoxItem. Simplest would be to capture the SelectionChanged event of your ListBox and in the handler set the focus on the currently selected item like below:
private void MyListBox_OnSelectionChanged(object sender, RoutedEventArgs e)
{
var item = (ListBoxItem)MyListBox.ItemContainerGenerator.ContainerFromItem(MyListBox.SelectedItem);
if(item !=null)
item.Focus();
}

Trying to bind datatable with listbox...something wrong

Please help...what am I doing wrong here? Trying to bind listbox to datatable. After debugging, i see data rows in the table but some how it is not binding to listbox.
FYI. _this is the name of my current window...
<ListBox Grid.Column="1" ItemsSource="{Binding ElementName=_this, Path=MainCategoriesTable}" HorizontalAlignment="Center" BorderBrush="Transparent" Background="Transparent" x:Name="lbMainCategories">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<RadioButton Grid.Column="0" Content="{Binding Path=main_category_name}" VerticalAlignment="Center" GroupName="grpMainCategory" x:Name="rdbEnableDisable" />
<Label Grid.Column="1" Width="30" Background="Transparent" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Below is the property trying to bind with...
public DataTable MainCategoriesTable
{
get { return _dtMainCategory; }
set { _dtMainCategory = value; }
}
For XAML to set the data context tocode behind this is what works for me
DataContext="{Binding RelativeSource={RelativeSource Self}}"
in code behind
this.DataContext = this;
But I used _this like you have used it successfully.
Set Presentation.Trace = High in the all XAML binding. That is not the exact syntax but if you start with Presentation it should be obvious.
Why no binding on label.
main_category_name is a public property? I notice it is in lower case.
DataTable works like a dictionary, not like an object. It doesn't expose your columns as properties, but each DataRow exposes an indexer that can be used to get the cell value. Therefore, you need to use indexer syntax:
<RadioButton Grid.Column="0" Content="{Binding Path=[main_category_name]}" VerticalAlignment="Center" GroupName="grpMainCategory" x:Name="rdbEnableDisable" />
UPDATE
Another thing that bothers me is that your MainCategoriesTable property doesn't notify about changes. If it's changed after all Bindings have been initialized, it won't work (while DependencyProperty will, because it always notifies about changes). To make it work, your context class must implement INotifyPropertyChanged interface and your property must look like this:
public DataTable MainCategoriesTable
{
get { return _dtMainCategory; }
set
{
if(value == _dtMainCategory)
{
return;
}
_dtMainCategory = value;
var h = this.PropertyChanged;
if(h != null)
{
h(this, new PropertyChangedEventArgs("MainCategoriesTable"));
}
}
}

ObservableCollection Images in Listbox to Content Control master detail WPf

I have an observablecollection of Images that get populated via the following code:
<StackPanel Orientation="Horizontal" Grid.Column="0">
<ListBox ItemsSource="{Binding BigImageView}" IsSynchronizedWithCurrentItem="True"
SelectedIndex="0" SelectedItem="{Binding CurrentItem}" />
</StackPanel>
<ContentControl Name="Detail" Content="{Binding BigImageView, Mode=OneWay}"
Margin="9,0,0,0" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"/>
However the Content Control is supposed to bind to the BigImageView via an ObservableCollection
BigImage = new ObservableCollection<Image>();
_listView = CollectionViewSource.GetDefaultView(BigImage);
_listView.CurrentChanged += new EventHandler(OnCurrentChanged);
public System.ComponentModel.ICollectionView BigImageView
{
get
{
return _listView;
}
set
{
_listView = value;
OnPropertyChanged("BigImageView");
}
}
I want to return the image to the content control when I move the listbox. I have been racking my brain and trying everyhitn but it does not work. any help would be appreciated.
There is no need to bind the selecteditem, the collectionview should take care of that.
Try this:
<ListBox ItemsSource="{Binding BigImageView}" IsSynchronizedWithCurrentItem="True" />
<ContentControl Name="Detail" Content="{Binding BigImageView, Mode=OneWay}" VerticalAlignment="Top">
<ContentControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
<ContentControl.ContentTemplate>
1
Create a viewmodel with a list and a selected item:
public class BigImageViewModel : INotifyPropertyChanged
{
private string bigImage;
//string for path?
public ObservableCollection<string> BigImageView {get; set; } //Of course, make sure it has a value
public string SelectedBigImage
{
get { return bigImage; }
set { bigImage = values; NotifyPropertyChanged("SelectedBigImage"); }
}
}
Set this object on the DataContext of your control in the constructor:
DataContext = new BigImage(); //Make sure you initialize your list
Set the ListBox ItemsSource to your BigImage list, bind your SelectedItem to BigImageView
and use that in your content control:
<ListBox ItemsSource="{Binding BigImageView}" SelectedItem={Binding SelectedBigImage} />
ContentControl:
<ContentControl Name="Detail" Content="{Binding SelectedBigImage, Mode=OneWay}" VerticalAlignment="Top">
<ContentControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding}"/> <!-- Nice template for showing your string BigImage -->
</DataTemplate>
<ContentControl.ContentTemplate>
</ContentControl>
2
Or screw that view model:
Set the list directly in the constructor (after the InitializeComponent() ):
myListBox.ItemsSource = ObservableCollection<string>(); //Make sure you initialize your list with whatever your object is..
Give the list a name:
And bind with an ElementName binding to your selected item:
<ContentControl Name="Detail" Content="{Binding ElementName=myListBox, Path=SelectedItem}" VerticalAlignment="Top">
<ContentControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding}"/> <!-- Nice template for showing your string BigImage -->
</DataTemplate>
<ContentControl.ContentTemplate>
</ContentControl>

Looking for a WPF ComboBox with checkboxes

My google skills fail me. Anyone heard of a control like that for WPF. I am trying to make it look like this (winforms screenshot).
You can do this yourself by setting the DataTemplate of the combo box. This article shows you how - for a listbox, but the principle is the same.
Another article here is perhaps a better fit for what you are trying to do, simple set the first column of the item template to be a checkbox and bind it to a bool on your business object.
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}"
Width="20" />
<TextBlock Text="{Binding DayOfWeek}"
Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
There is my combobox. I use Martin Harris code and code from this link Can a WPF ComboBox display alternative text when its selection is null?
<ComboBox Name="cbObjects" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" Margin="2,2,6,0" SelectionChanged="OnCbObjectsSelectionChanged" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" Width="20" VerticalAlignment="Center" Checked="OnCbObjectCheckBoxChecked" Unchecked="OnCbObjectCheckBoxChecked" />
<TextBlock Text="{Binding ObjectData}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock IsHitTestVisible="False" Name="tbObjects" Text="Выберите объекты..." Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" Margin="6,2,6,0" />
Small class for datasource:
public class SelectableObject <T> {
public bool IsSelected { get; set; }
public T ObjectData { get; set; }
public SelectableObject(T objectData) {
ObjectData = objectData;
}
public SelectableObject(T objectData, bool isSelected) {
IsSelected = isSelected;
ObjectData = objectData;
}
}
And there is two handler - one for handling CheckBox clicked and one for forming Text for ComboBox.
private void OnCbObjectCheckBoxChecked(object sender, RoutedEventArgs e) {
StringBuilder sb = new StringBuilder();
foreach (SelectableObject<tblObject> cbObject in cbObjects.Items)
{
if (cbObject.IsSelected)
sb.AppendFormat("{0}, ", cbObject.ObjectData.Description);
}
tbObjects.Text = sb.ToString().Trim().TrimEnd(',');
}
private void OnCbObjectsSelectionChanged(object sender, SelectionChangedEventArgs e) {
ComboBox comboBox = (ComboBox)sender;
comboBox.SelectedItem = null;
}
For ComboBox.ItemsSource I use
ObservableCollection<SelectableObject<tblObject>>
where tblObject is type of my object, a list of which I want to display in ComboBox.
I hope this code is useful to someone!
Give a try to CheckComboBox from Extended WPF Toolkit.
The main advantage for me is having two lists for binding:
all items available for selection
just selected items
I find this approach more practical. In addition you can specify value and display members of the collections you're binding.
If you don't want to bring a bunch of other controls with CheckComboBox, you can get the source code of it, it's pretty straightforward (need to bring Selector class as well).
ComboBox with Checkboxes
<ComboBox Height="16" Width="15">
<CheckBox Content="First Checkbox" />
<CheckBox Content="Second Checkbox" />
<CheckBox Content="Third Checkbox" />
<TextBlock Text="Some Text" />
</ComboBox>
The provided answers surprisingly didn't work for me, I tried many variations and kept getting error messages about the checkbox not being part of combobox and the data context seemed to be broken.
In the end I didn't have to do anything involving data templates or any code behind and my bindings are working fine (not shown in example)
I must say I'm happy with how easy this turned out to be after reading all the answers.

Resources