Problems closing tabs - wpf

I've a WPF app organized with tabs. I added a small button to each tabitem header that allow the user to close the tab.
When the user click on the small button, I remove the tabItem from the tabControl.
tabControl.Items.Remove(tabItem);
As result of this the tabItem dissapears, and that is fine, but here comes the problem:
The TabItem is not visible(good), but it still exists(bad). If I put a timer inside, the timer execucutes his Tick, and more important, if I've a datagrid with 200.000 records and I close the Tab, The garbage collector don't release the memory as I expected.
I asked google about the problem and I've implemented some of the advices described. It didn't work.
Can anyone help me?
Thanks

This is what I've been using, and as far as I can tell it removes the tabitem from memory. The problem with leaving a timer inside of the tabitem, is that the GC won't collect and dispose of it because it detects that the timer is still in use.
The Code:
namespace Reports.Controls
{
/// <summary>
/// Interaction logic for Test.xaml
/// </summary>
public partial class ReportTab : TabItem
{
public delegate void CloseEvents(ReportTab TabIndex);
public event CloseEvents Closing;
public ReportTab(string Title)
{
InitializeComponent();
tbTitle.Text = Title;
}
private void Image_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Closing(this);
}
}
}
The xaml:
<TabItem x:Class="Reports.Controls.ReportTab"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Main" Name="tbTitle" Margin="0,0,8,0"/>
<Image Height="13"
Source="pack://application:,,/Images/Icons/close.png"
MouseLeftButtonUp="Image_MouseLeftButtonUp"/>
</StackPanel>
</TabItem.Header>
<Grid>
//Tabitem stuff
</Grid>
</TabItem>
Here's the page with the Tabcontrol to add a tab:
void AddTab(string Title)
{
Controls.ReportTab rt = new Controls.ReportTab(Title);
rt.Closing += new Controls.ReportTab.CloseEvents(rt_Closing);
tabControl.SelectedIndex = tabControl.Items.Add(rt);
}
/// <summary>
/// Moves the Tab Control back to the Main tab
/// after a tab is removed
/// </summary>
/// <param name="TabIndex"></param>
void rt_Closing(Controls.ReportTab TabIndex)
{
tabControl.Items.Remove(TabIndex);
//This resets the tabcontrol back to it's first tabindex
tabControl.SelectedIndex = 0;
}

Place a user control in your tab, and in the Usercontrol code, handle its own "Unloaded" event.
In there you should be able to clean-up. (unless your timer is preventing the control from unloading, but last time I tried something similar it worked).
And yes, WPF is very dangerous for all those things, if you are not careful you can bleed controls pretty fast...

Related

How to get all windows close when you select 'Close All Windows' on the app's taskbar icon?

So I've been doing research on this for quite a few weeks now, and haven't really come up with an answer on why this doesn't work properly... I've even researched JumpLists to see if this was what I was looking for, but also to no avail. This problem relates to when you attempt to select 'Close All Windows' by right clicking an app's icon on the task bar...
For example, here is an EXTREMELY small and simple WPF application I wrote to demonstrate the problem I am having. Here is the app's icon in the task bar with its choices on the context menu for it...
contextmenutoolbar
I am selecting the choice 'Close all windows', for reference (the bottom one, with the X to the left of it).
This is a WPF application and here is the code for App.xaml:
<Application x:Class="CloseAllWindows.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Application.Resources>
</Application.Resources>
</Application>
Here is App.xaml.cs, which launches the MainWindow. It also sets the application's MainWindow property to the MainWindow that is instantiated. It also sets ShutdownMode to be only when the main window is closed... I don't want the application to still run if the main window is closed and some secondary windows are left open.
using System.Windows;
namespace CloseAllWindows
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
ShutdownMode = ShutdownMode.OnMainWindowClose;
var mainWindow = new MainWindow();
Application.Current.MainWindow = mainWindow;
mainWindow.Show();
}
}
}
Here is the code for MainWindow.xaml:
<Window x:Class="CloseAllWindows.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:CloseAllWindows"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Content="NewWindow" Click="ButtonBase_OnClick"></Button>
</StackPanel>
</Window>
And here is the code behind for it... which launches a secondary window when I click a button. It is setting the parent window (Owner property) to the main window, like all the examples I've seen say it should be set, and then call Show() on it.
using System;
using System.ComponentModel;
using System.Windows;
namespace CloseAllWindows
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var childWindow = new ChildWindow {Owner = this};
childWindow.Show();
}
}
}
Here is the code for the child window, ChildWindow.xaml:
<Window x:Class="CloseAllWindows.ChildWindow"
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:CloseAllWindows"
mc:Ignorable="d"
Title="ChildWindow" Height="300" Width="300">
<Grid>
</Grid>
</Window>
And it's corresponding code behind, ChildWindow.xaml.cs:
using System;
using System.Windows;
namespace CloseAllWindows
{
/// <summary>
/// Interaction logic for ChildWindow.xaml
/// </summary>
public partial class ChildWindow : Window
{
public ChildWindow()
{
InitializeComponent();
}
}
}
As you can see, these classes do not do very much... it was the simplest example of code I could write that shows the problem I am having. So the issue is, if I select Close all windows from the task bar context menu, it never closes all the windows. Instead, it will close the one child window, and still leave the main window open. Interestingly, I hear the windows dialogue chime when I do this, almost like its getting interrupted by something, but I have no idea what.
It also appears to act very randomly... if I spawn 20 windows, it will sometimes close 6 of the windows, then all of them... sometimes it will close a few windows one by one, then close the rest... sometimes it will close all child windows and leave only the main window open. Needless to say, I am pretty baffled as to the behaviour since it doesn't seem to follow any noticeable pattern... any help greatly appreciated! And hopefully the example is good enough to explain what I am trying to get at....
Well you could add an event that will close every window the event is implanted in. try this example:
step 1: add a class to your project call it whatever you want, I called it CloseWindowListener, add this code to your class:
public static class CloseWindowListener
{
public static event EventHandler<EventArgs> ClosingWindows;
public static void CloseWindows()
{
var CWindows = ClosingWindows;
if (CWindows != null)
{
CWindows(null, EventArgs.Empty);
}
}
}
step 2: Add the event handler to the window you desire to close when it is called.
public partial class TestWindow1 : Window
{
public TestWindow1 ()
{
InitializeComponent();
CloseWindowListener.ClosingWindows += CloseWindowListener_ClosingWindows;
}
private void CloseWindowListener_ClosingWindows(object sender, EventArgs e)
{
this.Close();
}
}
step 3: simply call the event from your main window or where ever you want.
private void button_Click_1(object sender, RoutedEventArgs e)
{
CloseWindowListener.CloseWindows();
}

Tray icon context menu positioning in WPF application

I have a C# WPF .NET 4 application that has an icon in the system tray. I am currently using the well-discussed WPF NotifyIcon, but the problem I am having is not dependent on this control. The problem is that .NET 4 simply does not allow (for the most part) a WPF ContextMenu object to appear over the top of the Windows 7 taskbar. This example illustrates the problem perfectly.
XAML:
<Window x:Class="TrayIconTesting.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="100" Width="400">
<Window.Resources>
<ContextMenu x:Key="TrayContextMenu" Placement="MousePoint">
<MenuItem Header="First Menu Item" />
<MenuItem Header="Second Menu Item" />
</ContextMenu>
<Popup x:Key="TrayPopup" Placement="MousePoint">
<Border Width="100" Height="100" Background="White" BorderBrush="Orange" BorderThickness="4">
<Button Content="Close" Click="ButtonClick"></Button>
</Border>
</Popup>
</Window.Resources>
<StackPanel Orientation="Horizontal">
<Label Target="{Binding ElementName=UseWinFormsMenu}" VerticalAlignment="Center">
<AccessText>Use WinForms context menu for tray menu:</AccessText>
</Label>
<CheckBox Name="UseWinFormsMenu" IsChecked="False" Click="UseWinFormsMenuClicked" VerticalAlignment="Center" />
</StackPanel>
</Window>
Code:
using System.Drawing;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Forms;
using ContextMenu = System.Windows.Controls.ContextMenu;
namespace TrayIconTesting
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ContextMenuStrip winFormsContextMenu;
public MainWindow()
{
InitializeComponent();
this.TrayIcon = new NotifyIcon
{
Icon = new Icon("Bulb.ico"),
Visible = true
};
this.TrayIcon.MouseClick += (sender, args) =>
{
switch (args.Button)
{
case MouseButtons.Left:
this.TrayPopup.IsOpen = true;
break;
case MouseButtons.Right:
if (!this.UseWinFormsMenu.IsChecked.GetValueOrDefault())
{
this.TrayContextMenu.IsOpen = true;
}
break;
}
};
}
private void ButtonClick(object sender, RoutedEventArgs e)
{
this.TrayPopup.IsOpen = false;
}
private void UseWinFormsMenuClicked(object sender, RoutedEventArgs e)
{
this.TrayIcon.ContextMenuStrip = this.UseWinFormsMenu.IsChecked.GetValueOrDefault() ? this.WinFormsContextMenu : null;
}
private ContextMenu TrayContextMenu
{
get
{
return (ContextMenu)this.FindResource("TrayContextMenu");
}
}
private Popup TrayPopup
{
get
{
return (Popup)this.FindResource("TrayPopup");
}
}
private NotifyIcon TrayIcon
{
get;
set;
}
private ContextMenuStrip WinFormsContextMenu
{
get
{
if (this.winFormsContextMenu == null)
{
this.winFormsContextMenu = new ContextMenuStrip();
this.winFormsContextMenu.Items.AddRange(new[] { new ToolStripMenuItem("Item 1"), new ToolStripMenuItem("Item 2") });
}
return this.winFormsContextMenu;
}
}
}
}
To see the problem make sure that the tray icon is always visible and not part of that Win7 tray icon popup thing. When you right click on the tray icon the context menu opens ABOVE the taskbar. Now right click one of the standard Windows tray icons next to it and see the difference.
Now, left click on the icon and notice that it DOES allow a custom popup to open right where the mouse cursor is.
Checking the "Use WinForms..." checkbox will switch the app to use the old ContextMenuStrip context menu in the Windows.Forms assembly. This obviously opens the menu in the correct place, but its appearance doesn't match the default Windows 7 menus. Specifically, the row highlighting is different.
I have played with the Horizontal and VerticalOffset properties, and with the "right" values you can make the context menu popup all the way at the bottom right of the screen, but this is just as bad. It still never opens where your cursor is.
The real kicker is that if you build this same sample targeting .NET 3.5 it works just as expected. Unfortunately, my real application uses many .NET 4 features, so reverting back is not an option.
Anyone have any idea how to make the context menu actually open where the cursor is?
After a little more searching I stumbled across this question & answer. I never thought to try the ContextMenu property on the NotifyIcon! While not ideal it will work well enough until WPF address the fact that the system tray is a useful part of applications. It will really be a shame to lose all the binding and command routing features provided by the WPF ContextMenu though.
It feels wrong to accept my own answer though, so I'm going to leave this open for a few more days.
Well, I'm glad didn't mark this as answered because I found a slightly better option for me. I found this article that details how to add icons to the System.Windows.Forms.MenuItem object. Now with just a little code I have a menu that perfectly matches system context menus!

WindowsFormsHost steals focus on activating application even activating by application's other wpf form

To Reproduce my case (.net 4.0)
Create a WPF Application (MainWindow.xaml)
Add a Winform user control that contains a textbox (UserConrol1.cs - Winform)
Put UserControl1 into MainWindow.xaml with windowsformshost
Add another WPF Window that contains a textbox(wpf) to project (Window1.xaml)
Create and Show Window1 after MainWindow InitializeComponent
Your project is ready,
Run Project and set textbox focused in MainWindow.xaml (that in WindowsFormsHost)
Deactivate your application by opening a window (Windows file explorer ,notepad, winamp etc.)
Try to write in textbox that in Window1 window by clicking textbox with mouse
And you will see that you can't set focus on textbox in Window1 because MainWindow Texbox( in winformshost will steal your focus on you application got activating)
Any idea?
MainWindow.xaml
<Window x:Class="WinFormsHostFocusProblem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WinFormsHostFocusProblem"
xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
Title="MainWindow" Height="350" Width="525">
<Grid>
<my:WindowsFormsHost Focusable="False" >
<local:UserControl1>
</local:UserControl1>
</my:WindowsFormsHost>
</Grid>
</Window>
MainWindow.xaml.cs
namespace WinFormsHostFocusProblem
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Window1 window1 = new Window1();
window1.Show();
}
}
}
Window1.xaml
<Window x:Class="WinFormsHostFocusProblem.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WinFormsHostFocusProblem"
xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
Topmost="True"
Title="Window1" Height="300" Width="300" Background="Red">
<Grid>
<TextBox Height="25">asd</TextBox>
</Grid>
</Window>
Window1.xaml.cs
namespace WinFormsHostFocusProblem
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
}
I used my MSDN support contract to get an answer to this problem. The engineer was able to repro from yunusayd's sample and confirmed it is almost certainly a bug in WindowsFormsHost.
Thanks to yunus for the minimal repro sample and Keith at Microsoft for tackling the issue and providing a workaround in less than one day.
Workaround code follows. It works by using .NET reflection to change a private variable used in WindowsFormsHost and disable the trigger for the bug. According to the engineer I worked with, this relies on WPF internals, but he spoke with product team members and it should be safe to use. There's no guarantee of lack of side effects, of course, but so far I haven't found any problems in my testing with multiple WindowsFormsHosts in multiple WPF windows (maybe nesting would be trickier). I modified the original workaround to work generically with multiple windows. You can just as easily hardcode references to specific windows and named WindowsFormsHost controls in the Application_Deactivated event and skip the whole "LastActive" scheme and extension methods.
// App.xaml.cs: you must hook up to Application.Deactivated
void Application_Deactivated(object sender, EventArgs e)
{
foreach (Window w in windows)
{
foreach (var host in UI.DependencyObjectExtension.AllLogicalChildren(w).
Where(c => c is WindowsFormsHost))
{
FIELD_FOCUSED_CHILD.SetValue(host, null);
}
}
}
public readonly static FieldInfo FIELD_FOCUSED_CHILD = typeof(System.Windows.Forms.Integration.WindowsFormsHost).
GetField("_focusedChild", BindingFlags.NonPublic | BindingFlags.Instance);
public static class DependencyObjectExtension
{
/// <summary>
/// Returns a collection of o's logical children, recursively.
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public static IEnumerable<DependencyObject> AllLogicalChildren(this DependencyObject o)
{
foreach (var child in LogicalTreeHelper.GetChildren(o))
{
if (child is DependencyObject)
{
yield return (DependencyObject)child;
if (child is DependencyObject)
{
foreach (var innerChild in AllLogicalChildren((DependencyObject)child))
{
yield return innerChild;
}
}
}
}
}
}
We had a similar problem in one of our applications and found that upgrading to .net 4.5 seems to have fixed a good portion of our application's WPF/WinForms focus issues including a similar one to this.
In addition, the _focusedChild field no longer exists in the .net 4.5 version of WindowsFormsHost

Removing a UserControl added at runtime using a button within UserControl

I have seen a few posts addressing how to remove an UserControl that has been added during runtime, but my problem is a little different. I have a UserControl that consists of an image with a small "x" button on the top right corner that is used to remove itself (the UserControl) from its parent canvas. Also to note is that the UserControl is added during runtime when the user doubleclicks on a ListboxItem. I have a Click event handler for the top right corner button but this code is not running at all. I know this because I have a breakpoint in this code which is not reached when I click the button.
So,
Why isn't the click event of the remove button being handled?
Maybe there is a better way to implement this. Please advise.
Here's the code used for adding it:
private void MyListBox_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.OriginalSource.ToString() == "System.Windows.Controls.Border" || e.OriginalSource.ToString() == "System.Windows.Controls.Image" || e.OriginalSource.ToString() == "System.Windows.Controls.TextBlock")
{
Expression.Blend.SampleData.MyCollection.Dataset lbi = ((sender as ListBox).SelectedItem as Expression.Blend.SampleData.MyCollection.Dataset);
var new_usercontrol = new MyUserControl();
new_usercontrol.MyImageSourceProperty = lbi.Image;
MyCanvas.Children.Add(new_usercontrol);
Canvas.SetLeft(new_usercontrol, 100);
Canvas.SetTop(new_usercontrol, 100);
Canvas.SetZIndex(new_usercontrol, 100);
}
}
The following is the cs code for the UserControl:
public partial class ModuleElement : UserControl
{
public ImageSource MyProperty
{
get { return (ImageSource)this.image.Source; }
set { this.image.Source = value; }
}
public ModuleElement()
{
this.InitializeComponent();
}
private void RemoveButton_Click(object sender, RoutedEventArgs e)
{
((Canvas)this.Parent).Children.Remove(this);
}
}
The XAML:
<Grid x:Name="LayoutRoot">
<Image x:Name="image" />
<Button x:Name="RemoveButton" Content="X" HorizontalAlignment="Right" Height="17.834" Margin="0" VerticalAlignment="Top" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Click="RemoveButton_Click">
</Button>
</Grid>
Thanks in advance,
Bryan
So I tried your code here exactly except for some name changes and could not reproduce your issue. In my personal experience your issue here has to be that for some reason the event for the click isn't subscribed to properly. For this I would go into designer for the user control, wipe out the current event for the button and double click in the designer event textbox such that VS or Blend generates all the code necessary for a proper subscription.
I have created a sample based on your code here. Feel free to pull it down and take a look to see if you can find any inconsistencies.
As far as a better way to implement this, check out the good old MVVM pattern and the MVVM Light Toolkit. With this you can have a central ViewModel class that will handle all of your button commands and binding without code behind.

Is this WPF ProgressBar Odd render behaviour a Bug?

Hope someone can help.
I have a simple scenario where clicking checkboxes is driving a progress bar in WPF. The checkboxes are contained in a UserControl and the Progress bar is in a simple WPF client window.
On the user control I am using two dependency properties:
1) the existing Tag property has the value I wish to bind to the progress bar value and
2) a DP called CbCount which represents the total number of checkboxes.
The problem:
When the application runs the progress bar's progress shows as being 100% complete even though via Snoop I can see the value is in fact 0. Clicking on the checkboxes everything works fine as expected.
Code:
UserControl - within namespace ProgBarChkBxs:
public partial class ucChkBoxes : UserControl
{
#region CbCount
public static readonly DependencyProperty CbCountProperty =
DependencyProperty.Register("CbCount", typeof(double), typeof(ucChkBoxes),
new FrameworkPropertyMetadata((double)0));
/// <summary>
/// Gets or sets the CbCount property. This dependency property
/// indicates the number of checkBoxes
/// </summary>
public double CbCount
{
get { return (double)GetValue(CbCountProperty); }
private set { SetValue(CbCountProperty, value); }
}
#endregion
double _totalCount = 0;
double _numberChecked = 0;
double DEFAULT = 0;
public ucChkBoxes()
{
InitializeComponent();
this.Tag = DEFAULT;
this.Loaded += new RoutedEventHandler(ucChkBoxes_Loaded);
}
void ucChkBoxes_Loaded(object sender, RoutedEventArgs e)
{
if (this.ourContainer.Children.Count != 0)
{
_totalCount = this.ourContainer.Children.Count;
}
this.CbCount = (double)_totalCount;
}
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
if (e.OriginalSource.GetType() == typeof(CheckBox))
{
CheckBox cb = (CheckBox)e.OriginalSource;
if (cb.IsChecked == true) { _numberChecked++; }
if (cb.IsChecked != true) { _numberChecked--; }
//simple POC progress metric
this.Tag = (double)(_numberChecked / _totalCount * _totalCount);
}
}
}
XAML:
<UserControl x:Class="ProgBarChkBxs.ucChkBoxes"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="Auto" Width="Auto">
<StackPanel>
<TextBlock Text="Please select options" ></TextBlock>
<StackPanel Name="ourContainer"
CheckBox.Checked="CheckBox_Checked"
CheckBox.Unchecked="CheckBox_Checked">
<CheckBox>Fruit Juice</CheckBox>
<CheckBox>Coffee</CheckBox>
<CheckBox>Toast</CheckBox>
<CheckBox>Cereal</CheckBox>
<CheckBox>Grapefruit</CheckBox>
</StackPanel>
</StackPanel>
</UserControl>
The Client which just has the databindings is a simple window - the local namespace below refers to the project namespace xmlns:local="clr-namespace:ProgBarChkBxs", the meat of the code is:
<StackPanel>
<local:ucChkBoxes x:Name="chkBoxes"/>
<ProgressBar Name="pb" Background="Azure" Minimum="0" Height="30"
Value="{Binding ElementName=chkBoxes,Path=Tag }"
Maximum="{Binding ElementName=chkBoxes,Path=CbCount }"
/>
</StackPanel>
The really weird thing is if within the DP definition of the CbCount if I change the FrameworkPropertyMetadata to a really small value to say (double)0.001 the problem goes away.
I am running this on XP.
All help gratefully received - thanks.
Update:
I have been digging into this again as it gnaws at my sole (who said get a life!)
Things I did:
1) Adding a slider which also like progressBar inherits from RangeBase gives me the expected behaviour.
2) Spinning up reflector I can see the static ctor for ProgressBar sets the default value first to 100,
RangeBase.MaximumProperty.OverrideMetadata(typeof(ProgressBar), new FrameworkPropertyMetadata(100.0)); Should AffectMeasure?
whereas in the slider:
RangeBase.MaximumProperty.OverrideMetadata(typeof(Slider), new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
3) So we need another layout pass after a I set the ProgressBar.Value
Going back to my simple POC application if within a the progressBar loaded handler in the client window I jig the layout on the first run through:
this.Width += 1; //trigger another layout pass
Then, hey, presto it works.
So is this a bug?
I still do not fully understand though how the progressBar value which is calculated from Minimum and Maximum values is affected in this way and not the Slider - the default value of Maximum appears to be having an effect and it looks as if the ProgressBar default should affect the measure pass. (missing FrameworkPropertyMetadataOptions.AffectsMeasure.)
Can anyone help, either confirm my thinking or explain what is really happening here?
ucChkBoxes_Loaded method gets called after the progressbar gets rendered. When the progressbar gets rendered, Tag and CbCount are zero meaning that the progressbar will have min=0, max=0 and value=0, which is correctly drawn as as 100%. If you invalidate the progressbar, for example resize window it will show as 0%, since now Tag and CbCount have been updated.
To fix, don't wait until ucChkBoxes.Loaded() is called to initialize your control, do it in constructor or when initializing the DP for CbCount, for example.
public ucChkBoxes()
{
InitializeComponent();
this.Tag = DEFAULT;
if (this.ourContainer.Children.Count != 0)
{
_totalCount = this.ourContainer.Children.Count;
}
this.CbCount = (double)_totalCount;
}

Resources