First, let me state my intentions. This is the final result I am looking for.
Mockup Of Intent http://s18.postimg.org/x818zbfbb/image.png
The output of my code, however, fails to achieve this. Here's what I did. This is my MainWindow XAML.
<Window x:Class="App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="" Height="200" Width="400"
WindowStartupLocation="CenterScreen" Name="ViewData">
<Grid Name="splMain" Background="{StaticResource GreyGradient}"></Grid>
</Window>
And this is the C# code for the dynamic RowDefitions creation:
private void ViewImgData(SubPod subPod)
{
var wb = new WebBrowser();
wb.Navigate(subPod.Image.Src);
var rowDefinition = new RowDefinition();
rowDefinition.Height = GridLength.Auto;
splMain.RowDefinitions.Add(rowDefinition);
Grid.SetRow(wb, 0);
((MainWindow)System.Windows.Application.Current.MainWindow).splMain.Children.Add(wb);
}
This I am calling from over here. It is guaranteed that this foreach loop would run more than once. However, my output window shows only 1 image instead of 2 or more, that too not taking up the whole space of the Window.
foreach (SubPod subPod in pod.SubPods)
{
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => ViewImgData(subPod)));
}
Where have I mistaken? Thanks.
You add new rows, but place the new webbrowser component in the 0th row.
Try
Grid.SetRow(wb, splMain.RowDefinitions.Count-1);
instead, since you need to place new content in the new row.
EDIT:
To fit the grid height and width try adding this to your splMain grid
Width="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType={x:Type Window}}" Height="{Binding ActualHeight,RelativeSource={RelativeSource AncestorType={x:Type Window}}"
see also Stretch Grid to window size
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I want to add canvas elements by user input. Something like when a button is clicked, a new <Ellipse/> element is added to the XAML file, inside the Canvas.
<Canvas x:Name="GraphDisplayFrame" Grid.Column="1" Grid.Row="0" Grid.ColumnSpan="3" Grid.RowSpan="4">
<Ellipse
Width="50"
Height="50"
Stroke="Black"
StrokeThickness="2"
Canvas.Left="100"
Canvas.Top="100" />
</Canvas>
I'm new to WPF, i'm not sure if this is the right way to do this.
The other thing i'm trying is System.Windows.Media but manipulating the XAMl file looks easier and nicer, since then the locations of the drawings are anchored to the canvas. I'm not sure if i can achieve something similar with System.Windows.Media.
So my question is in the title, but I'm open to other suggestions.
You probably want to learn about Bindings in WPF. Let's say you want your Ellipses be added by user's input (e.g. on Button click) to your Canvas. I'm not sure about Canvas usage for that purpose (it hasn't auto-alignments for child elements), so I used WrapPanel instead (to allow it align items). And we need 2 Buttons (to Add and Remove Ellipses). And I add a Label to display current amount of Ellipses that we have.
XAML:
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Name ="mainWindow"
Title="Main Window"
Width="800"
MaxWidth="800"
Height="450"
MaxHeight="450">
<Grid x:Name="MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
</Grid.RowDefinitions>
<Label Content="{Binding ElementName=mainWindow, Path=EllipsesCount, UpdateSourceTrigger=PropertyChanged}"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Grid.Row="0"
Background="DimGray"
Foreground="White"
Margin="15,35" />
<Button x:Name="BtnAddEllipse"
Content="ADD ELLIPSE"
Grid.Row="1"
Margin="10, 25" FontSize="22" FontWeight="Bold"
Background="LightGreen"/>
<Button x:Name="BtnRemoveEllipse"
Content="REMOVE ELLIPSE"
Grid.Row="2"
Margin="10, 25" FontSize="22" FontWeight="Bold"
Background="IndianRed"/>
<WrapPanel Orientation="Horizontal"
Background="Gainsboro"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Column="1"
Grid.Row="0"
Grid.ColumnSpan="3"
Grid.RowSpan="4" >
<ItemsControl ItemsSource="{Binding ElementName=mainWindow, Path=Ellipses, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</WrapPanel>
</Grid>
</Window>
Here you see that Label.Content property is binded to some EllipsesCount property (you'll see it in code-behind below). Also as WrapPanel is binded to Ellipses property.
Code-behind: (for copypaste purpose)
using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WpfApp2
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
// Text for Label about Ellipses amount in collection
private object _ellipsesCount = "Current ellipses count: 0";
public object EllipsesCount
{
get => _ellipsesCount;
set
{
_ellipsesCount = "Current ellipses count: " + value;
// When we set new value to this property -
// we call OnPropertyChanged notifier, so Label
// would be "informed" about this change and will get new value
OnPropertyChanged(nameof(EllipsesCount));
}
}
// Collection for Ellipses
private ObservableCollection<Ellipse> _ellipses;
public ObservableCollection<Ellipse> Ellipses
{
get => _ellipses;
set
{
_ellipses = value;
OnPropertyChanged(nameof(Ellipses));
}
}
// Hanlder, which would notify our Controls about property changes, so they will "update" itself with new values
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// Just for random colors
private readonly Random random = new Random();
public MainWindow()
{
InitializeComponent();
// Initialize collection of Ellipses
Ellipses = new ObservableCollection<Ellipse>();
// Handle when collection is changed to update Label
// with a new amount of Ellipses
Ellipses.CollectionChanged += delegate
{
// Update counter of ellipses when new one added or existing removed
EllipsesCount = Ellipses.Count;
};
BtnAddEllipse.Click += delegate
{
// Create an Ellipse with random stroke color
var ellipse = new Ellipse
{
Width = 50,
Height = 50,
Margin = new Thickness(3),
Stroke = new SolidColorBrush(Color.FromRgb((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255))),
StrokeThickness = 3
};
// Add to collection of ellipses
Ellipses.Add(ellipse);
};
BtnRemoveEllipse.Click += delegate
{
// Check, that Ellipses collection isn't null and empty,
// so we can remove something from it
if (Ellipses?.Count > 0)
Ellipses.Remove(Ellipses.Last()); // Removing last element
};
}
}
}
So at result you see, actually, "content of collection of Ellipses", without adding Ellipses directly to window. Binding makes WrapPanel to use collection of Ellipses as source of child elements, that should be in that WrapPanel (instead of original my answer, where we add Ellipse to Canvas as Children).
ORIGINAL answer.
Yes, you can. For example (based on your XAML):
XAML (empty window):
<Window x:Class="WPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFApp"
mc:Ignorable="d">
<!-- No even Grid here -->
</Window>
Code-behind (check comments also):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Setting Window properties (they not exists in XAML)
// XAML: <Window ... Title="Main Window" Height="450" Width="800">...
this.Title = "Main Window";
this.Height = 450;
this.Width = 800;
// Create main Grid and register some its name
// XAML: ...
var mainGrid = new System.Windows.Controls.Grid();
this.RegisterName("MainGrid", mainGrid);
// Add row and column definitions (as Canvas below needs, at least 4 rows and 3 columns)
for (int i = 0; i < 4; i++)
{
mainGrid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition { Height = new GridLength(50, GridUnitType.Star) });
if (i < 3) // Needn't 4th column
mainGrid.ColumnDefinitions.Add(new System.Windows.Controls.ColumnDefinition { Width = new GridLength(50, GridUnitType.Star) });
}
// Create Canvas and register its name too
// XAML: ...
var canvas = new System.Windows.Controls.Canvas
{
// Just to be able see it at Window
Background = System.Windows.Media.Brushes.LightGray
};
this.RegisterName("GraphDisplayFrame", canvas);
canvas.SetValue(System.Windows.Controls.Grid.ColumnProperty, 1);
canvas.SetValue(System.Windows.Controls.Grid.RowProperty, 0);
canvas.SetValue(System.Windows.Controls.Grid.ColumnSpanProperty, 3);
canvas.SetValue(System.Windows.Controls.Grid.RowSpanProperty, 4);
// Create Ellipse (child canvas element)
// XAML: ...
var ellipse = new System.Windows.Shapes.Ellipse
{
Width = 50,
Height = 50,
Stroke = System.Windows.Media.Brushes.Black,
StrokeThickness = 2
};
ellipse.SetValue(System.Windows.Controls.Canvas.LeftProperty, 100D);
ellipse.SetValue(System.Windows.Controls.Canvas.TopProperty, 100D);
// Add child Ellipse to Canvas
canvas.Children.Add(ellipse);
// or you already can find Canvas by its name:
(this.FindName("GraphDisplayFrame") as System.Windows.Controls.Canvas).Children.Add(ellipse);
// Add Canvas to MainGrid. Find Grid by its registered name too
(this.FindName("MainGrid") as System.Windows.Controls.Grid).Children.Add(canvas);
// Set main Grid as window content
this.Content = mainGrid;
}
}
So, as you can see, XAML markuping is quite more compact, that code-behinded one.
I have a listview with 150 items. Each item has different background color. If I scroll down a vertical scroll bar of list view, I can separate items to three parts:
top hidden items, items is hidden on the top of listview.
displayed items.
bottom hidden items, items is hidden on the bottom of listview.
I want to save all items to image.
I try to implement base on this guide How to render a WPF UserControl to a bitmap without creating a window
MainWindow.xaml:
<Window x:Class="CaptureListEx.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CaptureListEx"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="80"/>
</Grid.RowDefinitions>
<ListView Grid.Row="0"
Name="ListViewCtrl"
Margin="10"
BorderThickness="1"
ItemsSource="{Binding listViewItem}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
</ListView>
<Button Grid.Row="1"
Width="60"
Height="30"
Content="Capture"
Name="Capture"
Click="Capture_Click"
>
</Button>
</Grid>
</Window>
MainWindow.xaml.cs:
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace CaptureListEx
{
public partial class MainWindow : Window
{
public ObservableCollection<string> listViewItem { get; set; } = new ObservableCollection<string>();
public MainWindow()
{
InitializeComponent();
DataContext = this;
for(int i = 0; i <= 150; i++)
{
string temp = "This is item" + i;
listViewItem.Add(temp);
}
}
private void Capture_Click(object sender, RoutedEventArgs e)
{
SaveFileDialog dlg = new SaveFileDialog()
{
DefaultExt = ".jpg",
Filter = "JPG image (*.jpg)|*.jpg|All files (*.*)|*.*"
};
Nullable<bool> result = dlg.ShowDialog();
if (result == false) return;
if (File.Exists(dlg.FileName) && new FileInfo(dlg.FileName).Length != 0)
File.Delete(dlg.FileName);
double actualWidth = ListViewCtrl.ActualWidth;
ListViewCtrl.Measure(new System.Windows.Size(Double.PositiveInfinity, Double.PositiveInfinity));
ListViewCtrl.Arrange(new Rect(0, 0, actualWidth, ListViewCtrl.DesiredSize.Height));
double actualHeight = ListViewCtrl.ActualHeight;
RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)ListViewCtrl.ActualWidth, (int)ListViewCtrl.ActualHeight, 96, 96, PixelFormats.Pbgra32);
renderTarget.Render(ListViewCtrl);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
using (System.IO.FileStream stream = new System.IO.FileStream(dlg.FileName, System.IO.FileMode.Create, System.IO.FileAccess.Write))
{
encoder.Save(stream);
}
}
}
}
I expect I will get the image of all items with background color as displayed in UI, but the actual I only get image of displayed items and bottom hidden items.
Problem: Bottom hidden items do not have background color format. Top hidden items are not in image.
Could someone please help me make the right image. Thanks!
I only get image of displayed items and bottom hidden items
This happens because ListView when not limited by container dimensions, will only try to resize itself to fit as many items as it can starting from the first displayed item. In other words it doesn't try to somehow display "top hidden items" (how you call them).
I am not sure if this behavior is possible to change to make true auto-sizable ListView, but an easy workaround is to scroll to the very first item, make a screenshot and then restore position.
Add this before measuring (you will need FindChild method):
var scroll = ListViewCtrl.FindChild<ScrollViewer>();
var offset = scroll.VerticalOffset; // store offset
scroll.ScrollToTop();
Dispatcher.Invoke(() => { }, DispatcherPriority.Background); // do events
And then after render restore position:
scroll.ScrollToVerticalOffset(offset);
Why "do events"? You have to wait until wpf actually perform scrolling before measuring. As many things in wpf that one is also deferred, i.e. doesn't occurs instantly.
"I have two or more dockingManager, and I want every FloatingWindows to be dockable on every dockingManager."
floatingWindows must be created within a dockingManager, but it must be possible to dock it into another separate docking manager.
so when I do drag inside the first dockingManager the default positions are displayed. (4 icons: top, left, bottom, right)
but when I drag the floatingWindows into the second dockingManager nothing appears.
<Grid>
<avalonDock:DockingManager x:Name="dock1" Width="300" Height="200">
<avalonDockLayout:LayoutRoot>
<avalonDockLayout:LayoutRoot.FloatingWindows>
<avalonDockLayout:LayoutAnchorableFloatingWindow>
<avalonDockLayout:LayoutAnchorablePaneGroup DockHeight="100" DockWidth="100">
<avalonDockLayout:LayoutAnchorablePane x:Name="_LayoutAnchorablePane" FloatingHeight="150" FloatingWidth="150">
<avalonDockLayout:LayoutAnchorableTitle="FloatingWindow">
<Grid Background="CornflowerBlue">
</Grid>
</avalonDockLayout:LayoutAnchorable>
</avalonDockLayout:LayoutAnchorablePane>
</avalonDockLayout:LayoutAnchorablePaneGroup>
</avalonDockLayout:LayoutAnchorableFloatingWindow>
</avalonDockLayout:LayoutRoot.FloatingWindows>
</avalonDockLayout:LayoutRoot>
</avalonDock:DockingManager>
<avalonDock:DockingManager x:Name="dock2" Width="300" Height="200" >
<avalonDockLayout:LayoutRoot>
</avalonDockLayout:LayoutRoot>
</avalonDock:DockingManager/>
I also tried to force it from code, but it does not work!
LayoutAnchorable mAnchorableChilder=null;
public MainWindow()
{
InitializeComponent();
mAnchorableChilder = new LayoutAnchorable();
mAnchorableChilder.Title = "Sample dynamic avalondock";
_LayoutAnchorablePane.Children.Add(mAnchorableChilder);
Grid grid = new Grid { Background = new SolidColorBrush(Colors.Green) };
mAnchorableChilder.Content = grid;
}
I want to make the window have the effect can change the whole window which has many elements evenly to white, as the window behind in the picture:
I use code like
public MainWindow()
{
this.Opacity = 0.5;
}
but it change to black
How to make it whole evenly change to white even when there're many Element in the Window and don't set the window Style to none?(Because set Window AllowTransparent seems have to set the Style to none at the same time)
I hope can using code to do it, because I want to do it dynamically.
(Or possibly it use UserControl but not Window to achieve this effect? maybe the UserControl use with the Window and set the UserControl to Transparent can do it
----After I try, I find UserControl doesn't have property AllowTransparent, so it seems imposible use this way )
Basically, you have two options:
Use white Background color on Window and change Opacity on the window children, so the white starts to shine through
<Window Background="White">
<Grid Opacity="{Binding WhiteOutVisibility}" Background="WhiteSmoke">
<YourContent/>
</Grid>
</Window>
Use a white overlay control with alpha or Opacity that lets the actual content shine through
<Grid>
<YourContent/>
<Border Background="#80ffffff" Visibility="{Binding WhiteOutVisibility}"/>
</Grid>
In my opinion, you should use a white overlay if you want to block user interaction with the window content and white background if you want to continue user interaction.
If you need to fade only the client area, you can just put overlay - some empty semitransparent control over all the content on the window.
You can achieve this effect by laying a canvas over your window, and setting the background to white and an opacity value. Some xaml like this will work. Just change the UserControl for Window.
<UserControl x:Class="View.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="40" d:DesignWidth="100">
<Grid>
<TextBox Text="Hello there" />
<!-- this will show faintly -->
<Canvas Background="White" Opacity="0.8"></Canvas>
</Grid>
</UserControl>
This xaml looks like this:
The Window type has property AllowsTransparency. You can find it property on your window properties in MSVisualStudio. This can solve your problem.
Thanks for Phillip Ngan and grek40 s' answer,
both Grid and Canvas with background white and opacity works,
I write some test code that can show the effect
Xaml Part
<Window x:Class="WPFAbitraryTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button Grid.Row="0" Background="Blue" Foreground="White" FontSize="20" Click="SwitchOpacity_OnClick">Clcik to SwitchOpacity</Button>
<Button Grid.Row="1" Background="ForestGreen">hi2</Button>
<ListBox Grid.Row="2" Background="Orange">
<ListBoxItem>ListBox Item #1</ListBoxItem>
<ListBoxItem>ListBox Item #2</ListBoxItem>
<ListBoxItem>ListBox Item #3</ListBoxItem>
</ListBox>
<!-- <Grid Grid.Row="1" Grid.RowSpan="2" Opacity="0.9" Background="WhiteSmoke"/> -->
<Canvas Name="WhiteMaskCanvas" Grid.Row="1" Grid.RowSpan="2" Background="White" Opacity="0.5"></Canvas>
</Grid>
</Window>
.
Class Part
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void SwitchOpacity_OnClick(object sender, RoutedEventArgs e)
{
int opacityVal = 0;
Task.Factory.StartNew(() =>
{
for (int i = 0; i <= 1000; i++)
{
int j = 0;
Thread.Sleep(100);
//Use ++ % to change Opacity
this.Dispatcher.Invoke(
DispatcherPriority.SystemIdle,
new Action(() =>
{
WhiteMaskCanvas.Opacity = ++opacityVal % 10 / 10.0;
}));
////Use Abs Cosine to Change Opacity
//this.Dispatcher.Invoke(
// DispatcherPriority.SystemIdle,
// new Action(() =>
// {
// WhiteMaskCanvas.Opacity =
// Math.Abs(Math.Sin(++opacityVal*0.1)) ;
// }));
}
});
}
}
.
The Result:
.
further code,
if want to make the canvas mask whole window, you can change the canvas to
<Canvas Name="WhiteMaskCanvas" Grid.Row="0" Grid.RowSpan="3" Background="White" Opacity="0.5"></Canvas>
and add code to class:
public MainWindow()
{
InitializeComponent();
WhiteMaskCanvas.Visibility = Visibility.Collapsed;
}
private void SwitchOpacity_OnClick(object sender, RoutedEventArgs e)
{
WhiteMaskCanvas.Visibility = Visibility.Visible;
int opacityVal = 0;
Task.Factory.StartNew(() =>
{
//below same as code above
I have been looking for over 30 minutes now, but I simply cannot figure out what the problem is.
I have a TabControl and its items are to be closed by the user. Since each TabItem is in a way connected to a custom control and several objects that each use quite a lot of memory, I would like to dispose all objects that are used together with this TabItem.
To make it clearer and save you a lot of code here the simplified situation:
<UserControl x:Class="myProject.GridFour"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ScrollViewer Height="Auto" Margin="0" Name="scrollViewer11" Width="Auto" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"></ScrollViewer>
<ScrollViewer Grid.Column="1" Height="Auto" Name="scrollViewer12" Width="Auto" Margin="0" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"></ScrollViewer>
<ScrollViewer Grid.Row="1"> Height="Auto" Name="scrollViewer21" Width="Auto" Margin="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"></ScrollViewer>
<ScrollViewer Height="Auto" Name="scrollViewer22" Width="Auto" Grid.Column="1" Margin="0" Grid.Row="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"></ScrollViewer>
</Grid>
</UserControl>
Now I set the content of the corresponding tabitem:
GridFour myControl = new GridFour();
myTabItem.Content = myControl;
Also I have custom objects that each contain a grid, which is added as content to the scrollviewers of my user control:
class MyClass
{
internal Grid _grid = new Grid();
internal Image _image = new Image() {Width = Double.NaN, Height = Double.NaN HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, Stretch = Stretch.Fill};
//... a lot of variables, data, images and methods...
}
MyClass[] myObject = new MyClass[4];
for(int i = 0; i < 4; i++)
{
myObject[i] = new MyClass();
myObject[i]._grid.Children.Add(_image); //that actually happens when I initialise the object, it is just to show how everything is connected
}
myControl.scrollViewer11.Content = myObject[0]._grid;
myControl.scrollViewer12.Content = myObject[1]._grid;
myControl.scrollViewer21.Content = myObject[2]._grid;
myControl.scrollViewer22.Content = myObject[3]._grid;
Now when the user would like to close the tabitem obviously I would also like to get rid of myControl and of every single object myObject.
I tried to call the Dispose method on them via IDisposable but that always throws a NullReferenceException and I simply cannot figure out why.
I should maybe mention that every single myObject is within a Dictionary<string, MyClass> but I remove the object from there before I call dispose.
class MyClass : IDisposable
{
internal Grid _grid = new Grid();
internal Image _image = new Image() {Width = Double.NaN, Height = Double.NaN HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, Stretch = Stretch.Fill};
//... a lot of variables, data, images and methods...
public void Dispose()
{
// your custom disposing
_image = null; //or something like that
}
}
So you are removing a UI element and then trying to get the model to update, instead:
Why not bind the UI to a ObservableCollection list of your object. Then to remove you just need to remove from the list in the model (and dispose if you wish) but let the UI do it's job and simply relect the changes to the list. This is how WPF works best.
Read up on TabControl.ItemsSource Like here.