How to change data in a Silverlight DataContext Object - silverlight

I have a datacontext object that displays a list of uploaded files, and number of copies. So far, the user can click an edit icon that is in each row, and retrieve the data in that row, but I do not know how to change any data.
Here is the button event for the 'change details' dialog on the main page:
private void Image_MouseLeftButtonUp_1(object sender, MouseButtonEventArgs e)
{
Image b = sender as Image;
FileReceivedItem fi = b.DataContext as FileReceivedItem;
string fileCopies = fi.fileCopies;
string fileName = fi.FileName;
DetailsWindow detailsDlg = new DetailsWindow(fileCopies, fileName);
detailsDlg.Show();
detailsDlg.Closed += new EventHandler(detailsWnd_Closed);
}
public DetailsWindow detailsDlg;
void detailsWnd_Closed(object sender, EventArgs e)
{
MessageBox.Show("I want to change fileCopies here");
}
The relevant portions of the XAML:
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Hidden" ItemsSource="{StaticResource FileReceivedItem}" Name="filesReceivedList" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="1" Background="White" Margin="12,23,0,0"
VerticalAlignment="Top" Width="411" Height="175" Loaded="filesReceivedList_Loaded">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Width="404" Height="19" Orientation="Horizontal" >
<TextBlock x:Name="ListFileName" Text="{Binding FileName}" Width="283" VerticalAlignment="Center">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding FileName}"></ToolTip>
</ToolTipService.ToolTip>
</TextBlock>
<TextBlock Text="{Binding fileCopies}" Width="50" VerticalAlignment="Center"/>
<TextBlock Text="{Binding FileID}" Width="0" Visibility="Collapsed" VerticalAlignment="Center"/>
<Image Source="images/Edit.png" MouseLeftButtonUp="Image_MouseLeftButtonUp_1" Width="20" Margin="0 0 0 0">
<ToolTipService.ToolTip>
<ToolTip Content="Edit"></ToolTip>
</ToolTipService.ToolTip>
</Image>
<TextBlock Text="" Width="10" />
<Image Source="images/Delete.png" MouseLeftButtonUp="Image_MouseLeftButtonUp" >
<ToolTipService.ToolTip>
<ToolTip Content="Delete"></ToolTip>
</ToolTipService.ToolTip>
</Image>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Let me know if any important pieces of code are missing. I'm not sure how the data source works exactly.

I eventually got it to work. Pretty straightforward. I just haven't used SL in ahwile. This is what I did, although there might be a better way that someone might provide as a better answer:
void detailsWnd_Closed(object sender, EventArgs e)
{
changeFileCopies(detailsDlg.FileName, detailsDlg.nCopies.Text);
}
public void changeFileCopies(string filename, string newCopies)
{
var row = (FileReceivedItem)filesReceivedDataSource.Where(x => x.FileName == filename).FirstOrDefault();
row.fileCopies = newCopies;
filesReceivedList.ItemsSource = null;
this.filesReceivedList.ItemsSource = filesReceivedDataSource;
}

Related

How to display a new control through trigger in WPF

Now I got a Grid and I'm trying to display a control, lets say a StackPanel in the grid to cover the origin content when the mouse enters. I put the StackPanel in the first row, made its ZIndex=10(greater than Grid) and property Visibility binding to the Grid's IsMouseOver property. This trick just has one defect: the StackPanel will influence the grid's layout. For example, if the StackPanel's width is up to 500 and the original Grid only 100, the Grid expands quietly annoyingly. Here is the XAML snippet
<Grid x:Name="FileControlGrid">
!--The StackPanel to display when mouse enters--!
<StackPanel Orientation="Vertical" ZIndex="10" Grid.Row="0"
Visibility="{Binding ElementName=FileControlGrid, Path=IsMouseOver, Converter={StaticResource MouseoverToVisibilityCvt}}">
<...>
</StackPanel>
!--Origin Content below, I need the stackpanel to cover the Image--!
<Image Grid.Row="0" Source="{Binding FilePath, Converter={StaticResource FileiconCvt}}" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Center" MaxWidth="100" MaxHeight="100" Margin="5"/>
<TextBlock Grid.Row="1" Margin="0,5" Text="{Binding FileName, Mode=TwoWay}" FontFamily="Times New Roman" HorizontalAlignment="Left" FontWeight="SemiBold" MaxWidth="150" TextTrimming="WordEllipsis" FontSize="14"/>
</Grid>
I attempted using the Trigger, but instead of setting simple properties, I've no idea how to generate a grandly new control in triggers. Anyone can help?
Images here
Didn't understand the reason why you want to display the StackPanel in MouseOver but here is a simple solution that should work :
Create two properties of Visibility ImageVisiable and StackPanelVisiable.
Connect both properties to the Controls
MouseEnter event Switch between them
Example :
Xaml Side :
<Grid x:Name="Mouse" MouseEnter="Mouse_MouseEnter" MouseLeave="Mouse_MouseLeave">
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<!--The StackPanel to display when mouse enters-->
<StackPanel Orientation="Vertical" Visibility="{Binding StackPanelVisiable}" Grid.Row="0">
</StackPanel>
<!--Origin Content below, I need the stackpanel to cover the Image-->
<Image Grid.Row="0" Source="/Superman.jpg" Visibility="{Binding ImageVisiable}" Stretch="Fill" HorizontalAlignment="Left" VerticalAlignment="Center" MaxWidth="100" MaxHeight="100" Margin="5"/>
<TextBlock Grid.Row="1" Margin="0,5" Text="{Binding FileName, Mode=TwoWay}" FontFamily="Times New Roman" HorizontalAlignment="Left" FontWeight="SemiBold" MaxWidth="150" TextTrimming="WordEllipsis" FontSize="14"/>
</Grid>
View Model :
private Visibility m_stackVisibility;
private Visibility m_imageVisibility;
public MainVM()
{
m_stackVisibility = Visibility.Visible;
m_imageVisibility = Visibility.Hidden;
}
public Visibility StackPanelVisiable
{
get { return m_stackVisibility; }
set { SetProperty(ref m_stackVisibility , value); }
}
public Visibility ImageVisiable
{
get { return m_imageVisibility; }
set { SetProperty(ref m_imageVisibility, value); }
}
private void Mouse_MouseEnter(object sender, MouseEventArgs e)
{
((MainVM)this.DataContext).StackPanelVisiable = Visibility.Hidden;
((MainVM)this.DataContext).ImageVisiable = Visibility.Visible;
}
private void Mouse_MouseLeave(object sender, MouseEventArgs e)
{
((MainVM)this.DataContext).ImageVisiable = Visibility.Hidden;
((MainVM)this.DataContext).StackPanelVisiable = Visibility.Visible;
}
Hope it helped
Asaf

ListPicker issue in WindowsPhone8

Am using a list picker in a WindowsPhone App,I am not able to load the selected element in the list picker,the selected value shows the first item which was loaded during the page load,But the value we get in selection changed event is the correct one.Plz help me
XAML :
<Grid.Resources>
<DataTemplate x:Name="picker">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="0 0 0 0" FontSize="25" FontFamily="{StaticResource PhoneFontFamilyLight}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Name="PickerFullModeItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="0 0 0 0" FontSize="18" FontFamily="{StaticResource PhoneFontFamilyLight}"/>
</StackPanel>
</DataTemplate>
</Grid.Resources>
<toolkit:ListPicker ItemTemplate="{StaticResource PickerFullModeItemTemplate}" FullModeItemTemplate="{StaticResource PickerFullModeItemTemplate}" x:Name="list_city" Grid.Row="3" Grid.Column="5" Grid.ColumnSpan="2" VerticalAlignment="Top" SelectionChanged="list_city_SelectionChanged" Height="170" Grid.RowSpan="1" Margin="12,12,12,0" />
C# :
private void list_city_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int i = list_city.SelectedIndex;
string val = lst_cities[i]; //list of cities
}
Try this! It might work,
private void list_city_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int i = (sender as ListBox).SelectedIndex;
string val = lst_cities[i];
}
You need to bind the SelectedItem property of the ListBox to a property in your ViewModel using TwoWay databinding. If your property has the correct selected item you want when the ListBox loads then it will have the correct selection.

WPF & Prism 4.1 Garbage Collection / Memory Issues

I built a Prism application using WPF, .Net 4, Prism 4.1, and Unity. I'm using a DirectoryModuleCatalog to find modules at runtime. My views are displayed in a TabControl (MainRegion). When I remove a view from the region, the view and viewmodel remain in memory and never get garbage collected - the tabitem is removed. After many hours searching, I cannot figure out what I'm doing wrong.
Here's my bootstrapper:
public class Bootstrapper : UnityBootstrapper
{
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
}
protected override DependencyObject CreateShell()
{
var shell = new Shell();
return shell;
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog() { ModulePath = #".\Modules" };
}
}
Here's my module:
[Module(ModuleName = "ModuleA")]
public class Module : IModule
{
private IRegionManager _regionManager;
public Module(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void Initialize()
{
var view = new UserControl1();
//_regionManager.RegisterViewWithRegion("MainRegion", typeof(UserControl1));
_regionManager.Regions["MainRegion"].Add(view, "ModuleA");
_regionManager.Regions["MainRegion"].Activate(view);
}
}
And heres the viewmodel for my view that gets added to the region:
public class ViewModel
{
public DelegateCommand RemoveView { get; set; }
public ViewModel()
{
RemoveView = new DelegateCommand(() =>
{
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
var view = regionManager.Regions["MainRegion"].GetView("ModuleA");
regionManager.Regions["MainRegion"].Deactivate(view);
regionManager.Regions["MainRegion"].Remove(view);
});
}
}
And here's the code behind for the view:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
I've read that it could be because I'm instantiating the view in the module or perhaps the viewmodel in the view? When I use Red Gate Memory Profiler, and remove the view via the DelegateCommand, the view and viewmodel are both flagged as not being able to be garbage collected. Where is the reference that I'm not properly cutting?
Heres the retention graph from Ants: https://docs.google.com/file/d/0B4XjO9pUQxBXbGFHS1luNUtyOTg/edit?usp=sharing
Here's a test solution showing the issue.
Also, I posted the question on CodePlex as well.
It looks like you have a binding reference to it still in your retention graph.
Read and understand the following:
A memory leak may occur when you use data binding in Windows Presentation Foundation
I think it may be your problem, but you didn't show your actual bindings.
Finally found the root cause of my problem....
In our Shell.xaml we were binding IsDefault in one of our buttons to a PasswordBox's IsKeyboardFocused:
<Button Style="{DynamicResource RedSubmitButtonStyle}" IsDefault="{Binding ElementName=passwordBox1, Path=IsKeyboardFocused}" Command="{Binding LoginCommand}" Content="Login" Height="23" HorizontalAlignment="Left" Margin="145,86,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
IsKeyboardFocused, is a dependency property according to MSDN - so should be good on that end.
It was related to the attached property we had on the Password box that allows us to bind to the Password entered. The focus was remaining on that password box even after we hid the ChildWindow (from WPF toolkit extended). So instead of using IsDefault, I added a keydown event to the PasswordBox and if it was Key.Enter, I would change the focused UI control and log the person into the program.
Here's the full contents of our Shell.xaml file
<Grid x:Name="MainGrid" core:SharedResourceDictionary.MergedDictionaries="TabControlThemes;MenuThemes;ButtonThemes;DataGridThemes;TreeViewThemes;ComboBoxThemes;ListBoxThemes;GroupBoxThemes;ToggleSwitchThemes">
<DockPanel>
<ContentControl x:Name="menuContent" DockPanel.Dock="Top" prism:RegionManager.RegionName="MenuRegion" />
<ContentControl DockPanel.Dock="Bottom" prism:RegionManager.RegionName="FooterRegion" />
<ContentControl DockPanel.Dock="Top" prism:RegionManager.RegionName="MainContentRegion" />
</DockPanel>
<StackPanel Orientation="Horizontal" Panel.ZIndex="4" HorizontalAlignment="Right" VerticalAlignment="Top">
<Button Visibility="{Binding IsFullScreenToggleVisible, Converter={StaticResource visConv}}" Command="{Binding ToggleFullScreen}" Height="50" Name="button4" Width="70" HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top">
<Button.Content>
<TextBlock FontSize="12" FontWeight="Bold" TextWrapping="Wrap" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Text=" Toggle Full Screen" />
</Button.Content>
</Button>
<Button Visibility="{Binding IsAppCloseButtonVisible, Converter={StaticResource visConv}}" Command="{Binding ShutdownApplication}" Height="50" Name="button3" Width="50" HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top">
<Button.Content>
<Image Source="..\Graphics\close.png" Name="image1" />
</Button.Content>
</Button>
</StackPanel>
<xctk:ChildWindow Name="loginChildWindow" Panel.ZIndex="3" CloseButtonVisibility="Collapsed" FocusedElement="{Binding ElementName=usernameTextBox}" WindowStartupLocation="Center" WindowState="{Binding IsVisible, Mode=TwoWay, Converter={StaticResource boolConverter}}" IsModal="True" OverlayOpacity="1" Caption="Pioneer Login" Height="164" Width="261">
<xctk:ChildWindow.OverlayBrush>
<ImageBrush Stretch="None" Viewport="0,0,46,29" ViewportUnits="Absolute" ImageSource="../Graphics/escheresque.png" TileMode="Tile" />
</xctk:ChildWindow.OverlayBrush>
<xctk:BusyIndicator IsBusy="{Binding IsLoginBusy}" BusyContent="Authenticating...">
<Grid>
<TextBox GotFocus="usernameTextBox_GotFocus" Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="99,20,0,0" Name="usernameTextBox" VerticalAlignment="Top" Width="120">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding LoginCommand}" />
</TextBox.InputBindings>
</TextBox>
<Label Content="Username" Height="28" HorizontalAlignment="Left" Margin="28,18,0,0" Name="label1" VerticalAlignment="Top" />
<Label Content="Password" Height="28" HorizontalAlignment="Left" Margin="29,47,0,0" Name="label2" VerticalAlignment="Top" />
<PasswordBox attach:PasswordBoxAssistant.BindPassword="True" attach:PasswordBoxAssistant.BoundPassword="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="100,50,0,0" Name="passwordBox1" VerticalAlignment="Top" Width="120" />
<Button Style="{DynamicResource RedSubmitButtonStyle}" IsDefault="{Binding ElementName=passwordBox1, Path=IsKeyboardFocused}" Command="{Binding LoginCommand}" Content="Login" Height="23" HorizontalAlignment="Left" Margin="145,86,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
<Button Style="{DynamicResource RedSubmitButtonStyle}" Command="{Binding LazyLoginCommand}" Visibility="{Binding IsDebugMode, Converter={StaticResource visConv}}" Content="Quick Login" Height="23" HorizontalAlignment="Left" Margin="23,87,0,0" Name="button2" VerticalAlignment="Top" Width="89" />
</Grid>
</xctk:BusyIndicator>
</xctk:ChildWindow>
</Grid>

Print all content in ScrollViewer - Silverlight

I am showing some 100s of records in ScrollViewer Control. When I print the ScrollViewer Control it prints only the current view (10 records). How can I print all the 100s of data at once?
You might want to use PrintDocument class in Silverlight.
The usage is like..
in XAML file create List as
<ScrollViewer Height="300" VerticalScrollBarVisibility="Auto">
<ItemsControl x:Name="printSurface">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
Height="25">
<TextBlock Width="100"
Text="{Binding Name}" />
<TextBlock Width="75"
Text="{Binding Genre.Name}" />
<TextBlock Width="50"
Text="{Binding Price, StringFormat=c}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
And Code behind looks like.
void printButton_Click(object sender, RoutedEventArgs e)
{
PrintDocument doc = new PrintDocument();
doc.PrintPage += new EventHandler<PrintPageEventArgs>(doc_PrintPage);
doc.Print("Page title");
}
void doc_PrintPage(object sender, PrintPageEventArgs e)
{
// Stretch to the size of the printed page
printSurface.Width = e.PrintableArea.Width;
printSurface.Height = e.PrintableArea.Height;
// Assign the XAML element to be printed
e.PageVisual = printSurface;
// Specify whether to call again for another page
e.HasMorePages = false;
}

How to access Button programatically if the button is inside using C#

I have few questions as I am usign XAML for the first time.
How do I use the button (BrowseButton) to browse a folder in Harddrive ?
In this case as the button is inside
Can I use the way I have shown below?
Actually first dockpanel will hold the image and some label and the other dockpanel will have tabcontrol in it.
If I have a tabcontrol, How can I add a listview which can increase the tabs during runtime.. and the listview should be available in the runtime also.
How to add a close("X") mark on the top of the tab to close the button
probably I asked a lot of questions, sorry :(
Please help
<Grid>
<DockPanel>
<StackPanel>
<Image Name="imgClientPhoto" HorizontalAlignment="Left" VerticalAlignment="Top" Width="auto" Height="auto" Grid.Column="0" Grid.Row="0" Margin="0"
Source="D:ehtmp_top_left.gif" MinWidth="450" MinHeight="100" Grid.IsSharedSizeScope="True">
</Image>
<Button x:Name="BrowseButton" Margin="0,13.638,30,14.362" Content="Browse" Click="BrowseButton_Click" HorizontalAlignment="Right" Width="111" />
<TextBox x:Name="txtBxBrowseTB" Margin="46,10,146,10" Text="TextBox" TextWrapping="Wrap" TextChanged="BrowseTB_TextChanged"></TextBox>
<Label HorizontalAlignment="Left" Margin="-14,22,0,10" Name="label1" Width="69.75" FontSize="13" VerticalContentAlignment="Top" HorizontalContentAlignment="Center">Path:</Label>
</Grid>
</GroupBox>
</Grid>
</StackPanel>
</DockPanel>
<DockPanel>
<StackPanel Margin="0,158,0,0" HorizontalAlignment="Left" Width="330" Height="557.5" VerticalAlignment="Top">
<TextBox Height="50" Name="textBox1" Width="240" Margin="0,35,0,0" VerticalAlignment="Stretch" HorizontalAlignment="Right" />
<ListBox Height="470" Name="listBox1" Width="332" Background="LightGray" Margin="0,0,0,0" BorderBrush="IndianRed" BorderThickness="3" />
</StackPanel>
<TabControl Height="234" Name="tabControl1" Width="1035" Margin="0,0,0,0">
<TabItem Header="tabItem1" Name="tabItem1">
<Grid Height="144" />
</TabItem>
</TabControl>
</DockPanel>
</Grid>
1) The browse code should be something like this:
private void BrowseButton_Click(object sender, RoutedEventArgs e)
{
System.Windows.Forms.FolderBrowserDialog browse = new System.Windows.Forms.FolderBrowserDialog();
browse.RootFolder= Environment.SpecialFolder.MyDocuments;
browse.SelectedPath = "C:\\InitalFolder\\SomeFolder";
if (browse.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
txtBxBrowseTB.Text = browse.SelectedPath;
}
}
2) I have tried to simplify the xaml. Does this look like something you could go with?:
<Grid>
<Image Name="imgClientPhoto" Margin="0" Height="262" VerticalAlignment="Top" HorizontalAlignment="Left" ></Image>
<StackPanel VerticalAlignment="Stretch" >
<StackPanel VerticalAlignment="Top" Orientation="Horizontal" Height="30">
<Button Name="BrowseButton" Content="Browse" Click="BrowseButton_Click" HorizontalAlignment="Right" Width="111" />
<Label Name="label1" Width="69.75" FontSize="13" VerticalContentAlignment="Center" HorizontalContentAlignment="Right">Path:</Label>
<TextBox Name="txtBxBrowseTB" Width="200" Text="TextBox" VerticalContentAlignment="Center" TextWrapping="Wrap" TextChanged="BrowseTB_TextChanged"></TextBox>
</StackPanel>
<StackPanel>
<StackPanel Orientation="Vertical">
<TextBox Height="50" Name="textBox1" />
<ListBox Height="470" Name="listBox1" Background="LightGray" Margin="0,0,0,0" BorderBrush="IndianRed" BorderThickness="3" MouseLeftButtonUp="listBox1_MouseLeftButtonUp">
<ListBoxItem>User1</ListBoxItem>
<ListBoxItem>User2</ListBoxItem>
<ListBoxItem>User3</ListBoxItem>
<ListBoxItem>User4</ListBoxItem>
</ListBox>
</StackPanel>
<TabControl Name="tabControl1" />
</StackPanel>
</StackPanel>
</Grid>
You can also have a close button in the tab item Header. As many of the content properties in xaml controls, the content can actually be controls, and not necessary just a text.
<TabControl Name="tabControl1" >
<TabItem Name="tabItem1" >
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Label>TabHeader1</Label>
<Button Height="20" Width="20" FontWeight="Bold" Click="CloseTabButton_Click">X</Button>
</StackPanel>
</TabItem.Header>
<Grid Height="144">
<ListView />
</Grid>
</TabItem>
</TabControl>
3) Here is how you would add the tabs in C# dynamically:
private void listBox1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
//Get selected item
ListBoxItem item = (ListBoxItem)listBox1.SelectedItem;
string itemText = item.Content.ToString();
//Check if item is already added
foreach (TabItem tItem in tabControl1.Items)
{
if ((string)tItem.Tag == itemText)
{
//Item already added, just activate the tab
tabControl1.SelectedItem = tItem;
return;
}
}
//Build new tab item
TabItem tabItem = new TabItem();
tabItem.Tag = itemText;
//First build the Header content
Label label = new Label();
Button button = new Button();
label.Content = itemText;
button.Content = "X";
button.Height = 20;
button.Width = 20;
button.FontWeight = FontWeights.Bold;
button.Click += CloseTabButton_Click; //Attach the event click method
button.Tag = tabItem; //Reference to the tab item to close in CloseTabButton_Click
StackPanel panel = new StackPanel();
panel.Orientation = Orientation.Horizontal;
panel.Children.Add(label);
panel.Children.Add(button);
tabItem.Header = panel;
//Then build the actual tab content
//If you need only the ListView in here, you don't need a grid
ListView listView = new ListView();
//TODO: Populate the listView with what you need
tabItem.Content = listView;
//Add the finished tabItem to your TabControl
tabControl1.Items.Add(tabItem);
tabControl1.SelectedItem = tabItem; //Activate the tab
}
private void CloseTabButton_Click(object sender, RoutedEventArgs e)
{
//The tab item was set to button.Tag when it was added
Button button = (Button)sender;
TabItem tabItem = (TabItem)button.Tag;
if (tabItem != null)
{
button.Click -= CloseTabButton_Click; //Detach event handler to prevent memory leak
tabControl1.Items.Remove(tabItem);
}
}

Resources