I have a custom renderer in my iOS Native code for my main page. It works perfectly fine when the app start up, and renders Navbar items using the iOS System icons which is what I want. However, if I navigate away from the main page, when I navigate back the RightBarButtonItems array only contains two uninstantiated objects, I put in a check (RightNavItems.Title == null) to continue when this was the case to see what would happen, and indeed the items are not rendered, if I navigate away and back again the app crashes since the RightBarButtonItems array is now empty.
Why is it that the toolbar items are uninitialised when navigating back to the main page? What is the proper way to deal with navigation in a custom renderer like this?
Here is the code for the custom renderer:
public class ItemsPageRenderer : PageRenderer
{
public new ItemsPage Element
{
get { return (ItemsPage)base.Element; }
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
var rightNavList = new List<UIBarButtonItem>();
var navigationItem = this.NavigationController.TopViewController.NavigationItem;
for (var i = 0; i < Element.ToolbarItems.Count; i++)
{
var reorder = (Element.ToolbarItems.Count - 1);
var ItemPriority = Element.ToolbarItems[reorder - i].Priority;
UIBarButtonItem RightNavItems = navigationItem.RightBarButtonItems[i];
if (RightNavItems.Title == null)
continue;
if (RightNavItems.Title.ToLower() == "add")
{
rightNavList.Add(new UIBarButtonItem(UIBarButtonSystemItem.Add)
{
Action = RightNavItems.Action,
Target = RightNavItems.Target
});
}
else if (RightNavItems.Title.ToLower() == "edit")
{
rightNavList.Add(new UIBarButtonItem(UIBarButtonSystemItem.Edit)
{
Action = RightNavItems.Action,
Target = RightNavItems.Target
});
}
else
{
rightNavList.Add(RightNavItems);
}
}
navigationItem.SetRightBarButtonItems(rightNavList.ToArray(), false);
}
}
Check this code snippet
var rightNavList = new List<UIBarButtonItem>();
var navigationItem = this.NavigationController.TopViewController.NavigationItem;
for (var i = 0; i < Element.ToolbarItems.Count; i++)
{
var reorder = (Element.ToolbarItems.Count - 1);
var ItemPriority = Element.ToolbarItems[reorder - i].Priority;
UIBarButtonItem RightNavItems = navigationItem.RightBarButtonItems[i];
if (RightNavItems.Title == null)
continue;
if (RightNavItems.Title.ToLower() == "add")
{
rightNavList.Add(new UIBarButtonItem(UIBarButtonSystemItem.Add)
{
Action = RightNavItems.Action,
Target = RightNavItems.Target
});
}
else if (RightNavItems.Title.ToLower() == "edit")
{
rightNavList.Add(new UIBarButtonItem(UIBarButtonSystemItem.Edit)
{
Action = RightNavItems.Action,
Target = RightNavItems.Target
});
}
else
{
rightNavList.Add(RightNavItems);
}
}
navigationItem.SetRightBarButtonItems(rightNavList.ToArray(), false);
You change the item appearance from title to icon when first entering the page , however , when second entering the page , the condition RightNavItems.Title == null is true , so it jump out the loop without adding any item to the list , so navigationItem.SetRightBarButtonItems add a null array at last.
Solution
Modify as below
if (RightNavItems.Title == null)
{
rightNavList.Add(RightNavItems); //add this line.
continue;
}
Related
I've made a small tool bar that sits in a transparent form, it loads a variable sized menu from a text file and can be changed on the fly. Each button is a type of Label, the bar is just a list of buttons and adds/removes them in the correct spots. Width of the form is only a little bigger than the menu bar so that sub menu isn't cut off
Everything is working sweet except, when I reload everything part of the toolbar is lost. I've attempted to change the width so many ways, I've cleared and removed the controls from the form, refreshing the form/menu, updating it etc however nothing seems to make it work as intended EXCEPT if I call the reload function twice in a row, it works. I can't see why calling it once doesn't work but calling it twice works.
I'm fine with calling reload twice in a row as it would only be called a couple times a week.
Question: what on earth is causing this?
photo of issues first photo shows what it should look like, second is after removing a menu button and reloading, third is after adding a button and reloading
//calling this.reload() doesn't work
//calling this.reload();this.reload() works
void reload(Object o = null, EventArgs e = null)
{
this._menuBar.clear();
this.loadFromFile();
}
void loadFromFile(Object o = null, EventArgs e = null)
{
try
{
if (File.Exists("kpi.txt"))
{
string cline = "", cmenu = "", lhs = "";
menuList mb = null;
StreamReader sr = new StreamReader("kpi.txt");
while (!sr.EndOfStream)
{
cline = sr.ReadLine(); //get current line
if (cline.Length > 0 && cline[0] != ';')
{
//check if main menu/command
if (cline[0] == '[')
{
cmenu = Regex.Match(cline, #"(?<=^\[)[a-zA-Z -\#_{-~\^\r\n]+(?=\])").Value;
if (cmenu != "")
{
mb = this._menuBar.addMenuButton(cmenu);
mb.data["options"] = Regex.Match(cline, #"\/\w+$").Value;
var match = Regex.Match(cline, #"(?<=<)([^>\[\]\r\n]+)(?=>)");
mb.data["count"] = (match.Success ? match.Value : "0");
mb.data["copy"] = "";
applyMenuOptions(mb, false);
}
}
//just a standard line
else
{
cline = cline.Trim();
lhs = Regex.Match(cline, #"^[^\;\<\[\]\r\n]+(?=$|\<|\;)").Value;
if (mb.getSubMenuItem(lhs) == null)
{
var newButton = mb.addSubMenu(lhs);
if (newButton != null)
{
newButton.parent = mb;
newButton.data["options"] = mb.data["options"];
newButton.data["copy"] = Regex.Match(cline, #"((?<=\;)[^\[\]\<\r\n]+(?=<|$))").Value;
var matches = Regex.Match(cline, #"(?<=<)([^>\[\]\r\n]+)(?=>)");
int intout = 0;
if (int.TryParse(matches.Value, out intout))
{//no description
newButton.data["description"] = "";
newButton.data["count"] = intout.ToString();
}
else
{
newButton.data["description"] = matches.Value;
newButton.data["count"] = (matches.NextMatch().Success ? matches.NextMatch().Value : "0");
}
applyMenuOptions(newButton);
newButton.addMiddleClick(this.addcopy);
if (newButton.data["options"].Contains("i"))
{
newButton.addRightClick(this.appendInfo);
newButton.addRightClick(this.increment);
}
}
}
}
}
}
sr.Close();
this._menuBar.squish();
this.Width = this._menuBar.Width+50;
}
else
{
menuList mb = this._menuBar.addMenuButton("menu");
mb.data["options"] = "\\m";
mb.data["count"] = "0";
mb.data["copy"] = "";
mb.data["description"] = "";
applyMenuOptions(mb, false);
saveDictonary();
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to load data " + ex);
//ILog log = LogManager.GetLogger(typeof(Program));
//log.Info(ex);
}
}
public menuList addMenuButton(string s, int w = 0, int h = 0, int x = -1, int y = -1)
{
menuList mb = new menuList(this._form, s);
if (this.menuItems.Exists(z => z.Text == s)) return null;
mb.Width = (w==0?settings.intOf("ButtonWidth"):w);
mb.Height = (h==0?settings.IntOf("ButtonHeight"):h);
if (x == -1 || y == -1)
mb.Location = new Point(this.menuItems.Count > 0 ? this.menuItems.Last().Location.X + this.menuItems.Last().Width : padding);
else mb.Location = new Point(x, y);
mb.BringToFront();
mb.Show();
this.menuItems.Add(mb);
// this.Refresh();
return mb;
}
internal void clear()
{
foreach(var i in this.menuItems)
{
this._form.Controls.Remove(i);
i.clear();
i.Dispose();
}
this.menuItems.Clear();
this._form.Controls.Remove(this);
this.menuItems = new List<menuList>();
this._form.Controls.Add(this);
}
internal void squish()
{
try
{
this.Width = (this.menuItems.Count * this.menuItems.First().Width) + (2 * padding);
}
catch(Exception ex) { MessageBox.Show(""+ex); }
}
Found the culprit, bother the button class and the tool bar class were both adding themselves to the form control instead of button class adding to the tool bar (picture box) controls!
Removing transparency showed the buttons not moving when the tool bar was moved!
I a beginner in C# and WPF. I'm programming plugin for a node based software called vvvv. I have implemented sliders, buttons and other simple ui elements. The following code shows how a sliders node look in c# :
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Xml;
using VVVV.PluginInterfaces.V2;
namespace VVVV.Packs.UI.Nodes.WPF
{
[PluginInfo(Author = "lecloneur", Category = "WPF", Help = "WPF Slider", Name = "Slider", AutoEvaluate = false)]
public class WPFSlider : GenericNode, IPluginEvaluate
{
[Input("SizeX", DefaultValue = 120, Order = 9, MinValue = 0)]
public IDiffSpread<int> SizeX;
[Input("SizeY", DefaultValue = 120, Order = 9, MinValue = 0)]
public IDiffSpread<int> SizeY;
[Input("Orientation", Order = 1, DefaultEnumEntry = "Horizontal")]
public IDiffSpread<Orientation> OrientationIn;
[Output("Value", Order = 2)]
public ISpread<double> ValueOut;
int elements_count = 0;
public void Evaluate(int SpreadMax)
{
UIElementOut.SliceCount = SpreadMax;
ValueOut.SliceCount = SpreadMax;
for (int i = 0; i < SpreadMax; i++)
{
if (UIElementOut == null || !(UIElementOut[0] is Slider) || elements_count < SpreadMax || OrientationIn.IsChanged || SizeX.IsChanged || SizeY.IsChanged)
{
CreateElement(i);
}
OutputData(i);
Transformation(i, (Slider)UIElementOut[i]);
}
elements_count = SpreadMax;
}
private void CreateElement(int i)
{
UIElementOut[i] = new Slider();
var uiElement = (Slider)UIElementOut[i];
uiElement.Minimum = 0;
uiElement.Maximum = 1;
uiElement.Orientation = OrientationIn[i];
uiElement.IsMoveToPointEnabled = true;
uiElement.Width = SizeX[i]; ;
uiElement.Height = SizeY[i];
uiElement.VerticalAlignment = VerticalAlignment.Center;
uiElement.HorizontalAlignment = HorizontalAlignment.Center;
XmlReader XmlRead = XmlReader.Create("Styles/SliderStyle.xaml");
ResourceDictionary myResourceDictionary = (ResourceDictionary)XamlReader.Load(XmlRead);
XmlRead.Close();
Style uiElementStyle = myResourceDictionary["SliderStyle"] as Style;
uiElement.Style = uiElementStyle;
}
private void OutputData(int i)
{
var uiElement = (Slider)UIElementOut[i];
ValueOut[i] = uiElement.Value;
}
}
}
Now I'm trying to implement a tabcontrol where I could dynamically create tabitem and input UIElement into it. As far as I understand, I can only add one things to a tabitem. So I was thinking about creating a grid everytime I need to and fill it with all the incoming UIElement.
public void Evaluate(int SpreadMax)
{
SpreadMax = 1;
UIElementOut.SliceCount = 1;
for (var i = 0; i < SpreadMax; i++)
{
if (UIElementOut == null || !(UIElementOut[i] is TabControl))
UIElementOut[i] = new TabControl();
var uiElement = (TabControl)UIElementOut[i];
uiElement.Height = 200;
uiElement.Width = 500;
}
Grid grid;
int[] _elementCounts = new int[_elementInputs.SliceCount];
for (var i = 0; i < _elementInputs.SliceCount; i++)
{
if (_elementInputs[i] == null || !(_elementInputs[i] is UIElement))
{
grid = new Grid();
for (var j = 0; j < _elementInputs[i].IOObject.SliceCount; j++)
{
if (_elementInputs[i].IOObject[j] != null)
{
UIElement test = new UIElement();
test = _elementInputs[i].IOObject[j];
grid.Children.Add(test);
}
}
_elementCounts[i] = _elementInputs[i].IOObject.SliceCount;
ValueOut[i] = _elementCounts[i];
if (((TabControl)UIElementOut[0]).Items.Count <= i)
{
((TabControl)UIElementOut[0]).Items.Add(new TabItem { Header = _nameInputs[i].IOObject[0], Content = grid });
}
if (_nameInputs[i].IOObject.IsChanged)
{
((TabItem)((TabControl)UIElementOut[0]).Items[i]).Header = _nameInputs[i].IOObject[0];
}
if (_elementInputs[i].IOObject.IsChanged)
{
((TabItem)((TabControl)UIElementOut[0]).Items[i]).Content = grid;
}
}
}
for (var i = ((TabControl)UIElementOut[0]).Items.Count - 1; ((TabControl)UIElementOut[0]).Items.Count > _elementInputs.SliceCount; i--)
{
((TabControl)UIElementOut[0]).Items.RemoveAt(i);
}
}
I searched a lot but can't find any idea how to solve the error. Apparently adding elements to a the grid throw "specified element is already the logical child of another element";
Hi please try the next method on a several visual objects (child) and check if the resulted object is the same reference. Here is a usefull link with explanations and more...
Extension class code:
public static class VisualTreeHelperExtensions
{
public static T FindParent<T>(this DependencyObject child) where T : DependencyObject
{
while (true)
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
return parent;
child = parentObject;
}
}
}
Example:
var dataGrid1 = dependencyObject1.FindParent<DataGrid>();
var dataGrid2 = dependencyObject2.FindParent<DataGrid>();
var isSameObject = dataGrid1 == dataGrid2;
Updates
The grid can contains a large number of elements but the only last will be visible to user.
The error is coming from the elemnt you want to add itself, that elemnt is belong to another control (some another control has the element as a child).
Find the parent element of the element you want to add, remove the element from the current parent's children collection, and add this element as the new child of your grid.
Try to use snoop to figure out who is the parent of your element (containing in _elementInputs).
Here are some useful links (first, second).
Update 2
As I can understand you have a third party infrastructure in your project because I can't resolve the type of _elementInputs and UIElementOut collections.
Since the _elementInputs is a field, I still can't understand where is _elementInputs came from (can't see that in code).
The code to add a completely new element is wrong:
Correct code(in my opinion)
//here is in my opinion the correct algorithm in this case
var elementFromInputs = _elementInputs[i] as UIElement;
if (elementFromInputs == null) continue;
//try to find parent of type Panel
var parentPanel = elementFromInputs.FindParent<Panel>();
//try to find parent of type ContentControl
var parentContentControl = elementFromInputs.FindParent<ContentControl>();
if (parentPanel == null && parentContentControl == null)
{
//add if there is no any parents
grid.Children.Add(elementFromInputs);
}
if (parentPanel != null)
{
//remove element from parent's collection
parentPanel.Children.Remove(elementFromInputs);
}
if(parentContentControl != null)
{
//reset parent content to release element
parentContentControl.Content = null;
}
grid.Children.Add(elementFromInputs);
Update 3
You've pasted the code (from the correct code section) inside the if which condition is the _elementInputs[i] == null || !(_elementInputs[i] is UIElement) that means filtering all your UIElements out of the scope. Since I'm not familiar with the vvvv concepts I don't know what do you have inside the _elementInputs array, but if you have the UIElements there you need past the code I gave you before the if with condition _elementInputs[i] == null || !(_elementInputs[i] is UIElement).
Please update your question with the next clarifications:
1. What is inside the _elementInputs[i]?
2. What is inside the _elementInputs[i].IOObject?
3. What are UIElements you want to add to the grid?
4. Please run the next method and write me the comment what do you have in your grid and TabControl controls.
Test code
public void Evaluate(int SpreadMax)
{
SpreadMax = 1;
UIElementOut.SliceCount = 1;
for (var i = 0; i < SpreadMax; i++)
{
if (UIElementOut == null || !(UIElementOut[i] is TabControl))
UIElementOut[i] = new TabControl();
var uiElement = (TabControl)UIElementOut[i];
uiElement.Height = 200;
uiElement.Width = 500;
}
Grid grid = new Grid();
var listOfElements = new List<UIElements>
{
new Button {Background = Brushes.Tomato, Content = "Click Me"},
new Button {Background = Brushes.Yellow, Content = "Click Me"},
new Button {Background = Brushes.Green, Content = "Click Me"},
new Button {Background = Brushes.Blue, Content = "Click Me"}
};
listOfElements.ForEach(button => grid.Children.Add(button));
((TabControl)UIElementOut[0]).Items.Add(new TabItem { Header = "Objects", Content = grid });
}
I'll be glad to help if you will have problems with the code.
Regards.
When the following function is called, it is not showing the corresponding photoBoxes. I've done a debugging walkthrough, it even reaches the parts necessary to Show() and Hide(). I don't know what I can do. It's not showing anything
public void SmokerTakeIngredientFromTable(agents agent, List<smokers> smoker)
{
int index = 0;
bool smoker_takes_ingredients = false;
while (!smoker_takes_ingredients)
{
if ((smoker[index].item != agent.item_1) && (smoker[index].item != agent.item_2))
{
if (index == 0)
{
leftarrow_img.Show();
rightarrow_img.Hide();
downarrow_img.Hide();
}
else if (index == 1)
{
leftarrow_img.Hide();
rightarrow_img.Show();
downarrow_img.Hide();
}
else if (index == 2)
{
leftarrow_img.Hide();
rightarrow_img.Hide();
downarrow_img.Show();
}
agent.item_1 = 3;
agent.item_2 = 3;
break;
}
index++;
}
}
This is what the designer for these photoBoxes look like:
This is the properties page for one of the photoBoxes (they are all identical apart from the actual image file, they all have Visible = false too)
When making visibility changes I need to refresh the form using this.Refresh()
Hi everyone I use Visual Studio 2010 with WPF and C# with the model view presenter pattern.
I have some issues though. I have a search function which filters my observable collection based on some criteria the user inserts:
public void Search(string criteria)
{
if (!string.IsNullOrEmpty(criteria) && criteria.Length > 2)
{
var rez = from ua in _listAdress
where ua.Naziv.ToUpper().IndexOf(criteria.ToUpper()) >= 0 || ua.InventurniBroj.ToString() == criteria
select ua;
//var rez = from ua in _listAdress
// where ua.Naziv.ToUpper() == criteria.ToUpper() || ua.InventurniBroj.ToString() == criteria
// select ua;
//ListAdress = new ObservableCollection<Adress>(_adressRepository.FindByLookup(criteria));
UbrzajAdress = new ObservableCollection<Adress>(rez);
StatusText = string.Format("{0} zaduzenih pronađeno.", UbrzajAdress.Count);
}
else
{
//ListAdress = new ObservableCollection<Adress>(_adressRepository.ListOprema());
var rez = from ua in _listAdress
select ua;
UbrzajAdress = new ObservableCollection<Adress>(rez);
StatusText = "Prikaz svih zaduzenih.";
}
for (int i = 0; i < this.View.tabs.Items.Count; i++)
{
OpremaListView tab = (OpremaListView)this.View.tabs.Items[i];
if (tab != null && tab.Name != null && tab.Name.Equals("Sva Oprema"))
//if (tab.Header.Equals("Sva Oprema"))
{
OpremaListView opremaListView = (OpremaListView)tab.Content;
OpremaListPresenter opremaListPresenter = opremaListView.Presenter;
opremaListPresenter.refresh();
}
}
}
And here is the refresh method:
public void refresh()
{
this.View.dg.ItemsSource = _applicationPresenter.UbrzajAdress;
}
Now this all works. BUT, this is for a tab. Which is how most of my application is structured. Unfortunately due to the large amount of data and tabs working much slower in my app than a new window does my biggest list is shows in a new window in a WPF data grid.
Now when I try to alter my search to upgrade a window, even though it compiles I get no refresh, so when I enter a new row, and then search for that row, it is not shown.
Here is the search that I tried to change so it works for a window:
public void Search(string criteria)
{
if (!string.IsNullOrEmpty(criteria) && criteria.Length > 2)
{
var rez = from ua in _listAdress
where ua.Naziv.ToUpper().IndexOf(criteria.ToUpper()) >= 0 || ua.InventurniBroj.ToString() == criteria
select ua;
//var rez = from ua in _listAdress
// where ua.Naziv.ToUpper() == criteria.ToUpper() || ua.InventurniBroj.ToString() == criteria
// select ua;
//ListAdress = new ObservableCollection<Adress>(_adressRepository.FindByLookup(criteria));
UbrzajAdress = new ObservableCollection<Adress>(rez);
StatusText = string.Format("{0} zaduzenih pronađeno.", UbrzajAdress.Count);
}
else
{
//ListAdress = new ObservableCollection<Adress>(_adressRepository.ListOprema());
var rez = from ua in _listAdress
select ua;
UbrzajAdress = new ObservableCollection<Adress>(rez);
StatusText = "Prikaz svih zaduzenih.";
}
for (int i = 0; i < this.View.tabs.Items.Count; i++)
{
TestWindow tab = (TestWindow)this.View.tabs.Items[i];
if (tab != null && tab.Title != null && tab.Title.Equals("Sva Oprema"))
//if (tab.Header.Equals("Sva Oprema"))
{
TestWindow opremaListView = (TestWindow)tab.Content;
TestWindowPresenter opremaListPresenter = opremaListView.presenter;
opremaListPresenter.refresh();
}
}
}
This compiles but it doesn't refresh or do anything. The refresh is:
public void refresh()
{
this.View.dg.ItemsSource = _applicationPresenter.UbrzajAdress;
}
Another interesting thing is that if I search for an item before adding a new item and then show all oprema, by deleting everything in the search text box and then clicking search, the program crashes with the error:
Invalid Cast Exception was unhandled
Unable to cast object of type 'System.Windows.Controls.TabItem' to type 'Inventar.Views.OpremaListView'.
I'm guessing this has to do with there not being a tab open in the program and it therefore failing but I don't know how to fix it and any help would be greatly appreciated.
Anyway I just don't understand what to do and am looking for any help I can get. Thanks
I have a tabcontrol with 9 tabitems in it. Each tab has as series of TextBoxes for the user to enter data. At the bottom is a clear button, hooked up to this method:
public void ClearTextBoxes()
{
ChildControls ccChildren = new ChildControls();
foreach (object o in ccChildren.GetChildren(rvraDockPanel, 2))
{
if (o.GetType() == typeof(WatermarkTextBox) || o.GetType() == typeof(DateBox) ||
o.GetType() == typeof(DigitBox) || o.GetType() == typeof(PhoneBox))
{
WatermarkTextBox water = (WatermarkTextBox)o;
water.Text = "";
}
else if (o.GetType() == typeof(ComboBox))
{
ComboBox combo = (ComboBox)o;
combo.SelectedIndex = -1;
}
else if (o.GetType() == typeof(CheckBox))
{
CheckBox check = (CheckBox)o;
check.IsChecked = false;
}
}
}
This works perfectly fine, however I also have a MenuItem that allows the user to ClearAll tabs. Right now the clear button only clears what's on the currently selected tab, and leaves everything else alone. My thought on how to do this was to iterate through the tabitems with this loop:
for (int i = 0; i < 10; i++ )
{
tabSelection.SelectedIndex = i;
clearButton_Click(null, null);
}
It will flip through all the tabs, but won't clear anything. I have tried using Automation instead, with the same result. It just won't seem to clear anything.
ChildControls class:
class ChildControls
{
private List<object> listChildren;
public List<object> GetChildren(Visual p_vParent, int p_nLevel)
{
if (p_vParent == null)
{
throw new ArgumentNullException("Element {0} is null!", p_vParent.ToString());
}
this.listChildren = new List<object>();
this.GetChildControls(p_vParent, p_nLevel);
return this.listChildren;
}
private void GetChildControls(Visual p_vParent, int p_nLevel)
{
int nChildCount = VisualTreeHelper.GetChildrenCount(p_vParent);
for (int i = 0; i <= nChildCount - 1; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(p_vParent, i);
listChildren.Add((object)v);
if (VisualTreeHelper.GetChildrenCount(v) > 0)
{
GetChildControls(v, p_nLevel + 1);
}
}
}
}
WPF discards the entire VisualTree of a TabItem which isn't selected, so no controls actually exist on tabs that are not visible.
To maintain control values (such as Selections, Text, etc), they need to be bound to something. If they're bound to something then your Clear() method should actually clear the Bound values, not the UI values. If they're not bound to anything, then the TabControl will revert to its initial state when selected.
To get your code working you need to add the following line to your cleanup method:
tabControl.SelectedIndex = i;
--> UpdateLayout();
Button_Click(null, null);
The UpdateLayout method takes care that the TabItem is drawn and the VisualTree is available afterwards.
Generally this approach isn't nice and if you have a real Application with Business data behind I'd recommend you have a look at databinding/MVVM. The approach shouldn't be to reset your controls in the view, but to reset your bound business data in the background.