How To Set TabPage on start? - c#

I have a TabControl with three TabPages. The initial start of the app opens always the first TabPage on the left side. For me it's necessary to set the starting TabPage (for example the second one).
Of course, I know about possibilities to change the tab on start like these:
tabControl.SelectedTab = tabPage;
tabControl.SelectTab(tabPage);
...
But this code would also activate additional events to fire like TabControl.Selecting, TabControl.Deselecting, TabControl.SelectedIndexChanged etc. — I would really like to prevent this in advance.
What I am looking for is some kind of property in the TabControl like "StartingTabPageIndex" - setting it to 1 would open the second TabPage on start without invoking any unnecessary events.

Another option. Go into the Form Designer, change the SelectedIndex property from 0 to 1:
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Controls.Add(this.tabPage3);
this.tabControl1.Location = new System.Drawing.Point(223, 21);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 1; // <-- This Line
this.tabControl1.Size = new System.Drawing.Size(300, 143);
this.tabControl1.TabIndex = 3;
The event handlers aren't connected yet, and making any modifications to the TabControl in the designer doesn't seem to affect that property. It seems safe to change it this way.

You should remove the binding with the event handlers from the designer and add them after you have set the initial tabpage
After removing them in the designer (this doesn't delete the event handler code) rebind the event handler in the form load event after setting the required tabpage
tabControl.SelectedTab = tabPage;
tabControl.Selected += tabControl_Selected;
.... and so on for the other events to handle....

Update
I just made a simple test, and SelectedTab does not work because it expects the handle to be created on set.
However this seems to work:
public class MyTabControl : TabControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Browsable(true)]
public new int SelectedIndex
{
get { return base.SelectedIndex; }
set { base.SelectedIndex = value; }
}
}
You'll now be able to see SelectedIndex in the designer and can set it. It won't change the visible tab in the designer, but it will store the "initial tab index" (zero-based).
It does change SelectedIndex, but it does not call the events since events are assigned last in the designer's serialization, so they are never assigned before the change.
Old
One option would be having SelectedTab serialized. You'd only need to derive your own custom TabControl from TabControl and have something like this:
public class MyTabControl : TabControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public new TabPage SelectedTab {
get { return base.SelectedTab; }
set { base.SelectedTab = value; }
}
}
That way you'll get your designer selected SelectedTab as initial.
I haven't tested this, but theory says it should work :-)

Related

Execute method on tabpage created in code

I have searched but cannot locate this problem.
On form 1 in code, I create a TabPage with a usercontrol in it and then add the TabPage to form1.TabControl and call public method LoadData on the usercontrol.
Problem: I need to reload the data when the new tabpage is activated or gains focus. If I did not create the tabpage in code, I could simply use TabControl's selectedIndex change event, but it needs to be created in code.
How can I do this? Form 1:
private void CreateNewTab()
{
TabPage tp1 = new TabPage();
tp1.Text = "HSV";
tp1.Name = "tpHSV";
if (tabContMain.TabPages.ContainsKey(tp1.Name) == false)
{
HSVControl hsvc = new HSVControl();
hsvc.Dock = DockStyle.Fill;
hsvc.LoadData();
tp1.Controls.Add(hsvc);
tabContMain.TabPages.Add(tp1);
}
}
====EDIT===============
Thanks for the comments. Let me try to explain my problem better. The selectedIndex change event works fine. I can access the tab by it's text or name. The problem is calling the hsvc.LoadData() method. I need to recall this method when the tab containing hsvc user control is clicked. The LoadData() is public, but I cannot find a way to access it in Form1 (which holds the selectedIndex change event). I need a reference to hsvc control.
I added a property to the Form1 class like this:
private UserControl mControl;
then assigning it:
HSVControl hsvc = new HSVControl();
hsvc.Dock = DockStyle.Fill;
hsvc.LoadData();
mControl=hsvc;
Then calling it in SelectedIndex change event, but it is still not visible there.
Ok, thanks again for the help. The solution was kind of staring me in the face. I'm not sure it's the best, but this worked very well.
Created interface:
public interface IControlBase
{
void LoadData();
}
Had UserControl implement interface:
HSVControl : UserControl,IControlBase
and having the existing LoadData() method on the usercontrol.
change
private UserControl mControl;
to
private IControlBase mControl;
Then in SelectedIndex change:
mControl.LoadData();
TabControl Has a property called SelectedTab. use that property like:
private void CreateNewTab()
{
TabPage tp1 = new TabPage();
tp1.Text = "HSV";
tp1.Name = "tpHSV";
if (tabContMain.TabPages.ContainsKey(tp1.Name) == false)
{
HSVControl hsvc = new HSVControl();
hsvc.Dock = DockStyle.Fill;
hsvc.LoadData();
tp1.Controls.Add(hsvc);
tabContMain.TabPages.Add(tp1);
tabContMain.SelectedTab = tp1;
}
}
In that last line, It makes the TabControl to call its SelectedIndexChanged event. Then call LoadData event in this event:
private void TabControl_SelectedIndexChangedEvent(object sender, EventArgs e)
{
//So Because Hsvc is a public field. I call its method here:
if(TabControl.SelectedTab.Name = "My Desired Tab";
{
hsvc.LoadData();
}
}

WPF C# How to avoid event fire whle importing?

I have a WPF app with a Window and different UserControls are shown in it one by one no button clicks.
I import data from a file and all data is stored in a common object "ImportExportData". All UserControls are bind to respective Property (as custom objects like Data1, Data2...) of ImportExportData class.
In my USerControl I have combobox's for NumberZones proeprty those SelectionChanged event is handled respectively. In the SelectionChanged event of this combobox, based on the number selected that many rows are added to an ObservableCollection of Data2 property.
While importing data and setting the imported object (Data2) as the DataContext of USerControl2, it sets the NumberZones property value to the respective combobox and SelectionChanged event is fired as it should. At this time, the object already contains reqd rows in ObservableCollection and this event should not add it.
PArent window has a flag "importedData" that tells me that the object is imported. But I can't make that false once UserContrl2 is loaded, as their are their UC that will follow UC2. In UC2 I can create another flag "importing" and make it false once all UI is loaded. Thru which UC event can I know that UI is loaded and thus make "importing" as false ??
I am wondering how do I avoid from firing the SelectionChanged event when the imported object is populating the UI components. Which event of the UserControl will help me in this case maybe to keep a flag in USerControl2.
Any idea, suggestions please.
It is very hard to understand all of your question, so bear with me... I'll address each point that I understand.
Thru which UC event can I know that UI is loaded and thus make "importing" as false ??
Take a look at the FrameworkElement.Loaded Event page at MSDN.
I am wondering how do I avoid from firing the SelectionChanged event when the imported object is populating the UI components.
There are two way of achieving this goal... The first way does not stop the event from firing, but instead ignores it when data is being imported. basically involves temporarily unsubscribing from the SelectionChanged event and then re-subscribing to it. If I understand you correctly, you have a bool property in your parent Window and SelectionChanged handlers in your UserControls... first, you can add a bool property to each of your UserControls:
public bool CanChangeSelection { get; set; }
Now, in your parent Window (assuming that you have references to your controls) you can update your property:
private bool isImporting = false;
public bool IsImporting
{
get { return isImporting; }
set
{
isImporting = value;
UserControl1.CanChangeSelection = isImporting;
UserControl2.CanChangeSelection = isImporting;
...
UserControlN.CanChangeSelection = isImporting;
}
}
Then finally, in your control SelectionChanged handlers:
private void SelectionChangedHandler(object sender, RoutedEventArgs e)
{
if (CanChangeSelection)
{
// do your stuff in here
}
}
The second way basically involves temporarily unsubscribing from the SelectionChanged event and then re-subscribing to it. For this option, we need to change the definition of our new bool property in each of your UserControls:
private bool canChangeSelection = false;
public bool CanChangeSelection
{
get { return canChangeSelection; }
set
{
canChangeSelection = value;
if (!canChangeSelection)
{
if (SelectionChangedHandler != null) ComboBox1.SelectionChanged -=
SelectionChangedHandler;
}
else if (SelectionChangedHandler == null) ComboBox1.SelectionChanged +=
SelectionChangedHandler;
}
}
I personally prefer the first method as it is more straightforward.

WPF UserControl creating in code, Bindings not registering with INotifyPropertyChanged

I have created a Generic WPF Control, so the layout is all defined in code. This is all fine and I have added bindings which work first time round, for example:
private bool btEnabled { get { return SOME_LOGIC; } }
Button bt = new Button { Content = "Button" };
bt.SetBinding(Button.IsEnabledProperty, new Binding { Source = btEnabled, Mode = BindingMode.OneWay });
Whenever something happens which affects btEnabled property I am calling INotifyPropertyChanged but the PropertyChanged event is always null, I can't work out why the binding isn't registering the PropertyChanged event.
Any Ideas?
Thanks
I see that btEnabled is a property, that too private!
So presumably there are two mistakes...
Your Binding should set Path="btEnabled" not Source=btEnabled
And your btEnabled property should be public.

How to disable controls until a condition is met?

Currently in my program in about 10 control event handlers I have this code:
if (!mapLoaded)
return;
When I load a map through the open file dialog I set mapLoaded to true. Another way to do this would be to just disable all the controls for startup and after loading a map to enable all the controls. Unfortunately there are 30+ controls and this is just 30 lines of..
a.Enabled = true;
b.Enabled = true;
c.Enabled = true;
I can't really do a foreach loop through this.Controls either because some of the controls are menustrip items, toolstrip items, panel items, scrollbars, splitters, et cetera and that loop doesn't cover that.
Ideally there would be a way to set every control's enabled property to true in a single and simple loop but I'm not sure of how to do that. Any ideas SO?
Use data binding:
Change mapLoaded into a property that notifies observers when its value has changed...
public bool MapLoaded
{
get
{
return mapLoaded;
}
set
{
if (value != mapLoaded)
{
mapLoaded = value;
MapLoadedChanged(this, EventArgs.Empty);
}
}
}
private bool mapLoaded;
public event EventHandler MapLoadedChanged = delegate {};
// ^ or implement INotifyPropertyChanged instead
Data-bind your controls' Enabled property to MapLoaded. You can set up the data bindings either using the Windows Forms designer, or using code, e.g. right after InitializeComponent();:
a.DataBindings.Add("Enabled", this, "MapLoaded");
b.DataBindings.Add("Enabled", this, "MapLoaded");
c.DataBindings.Add("Enabled", this, "MapLoaded");
How about changing your opening strategy, have a new form that let's your user load a map, and the simply not load your main form until one has been loaded?

Custom control resize C#

I would like to resize custom control according to items it content
This dont work for me:
public CustomControl()
{
InitializeComponent();
if (ErrorLimits == false && Range == false)
{
this.Size = new Size(100, 100);
this.Invalidate();
}
else
{
this.Size = new Size(250,250);
this.Invalidate();
}
}
It changing nothing, How can I achieve it?
Thanks!
The containing form will instantiate CustomControl and then set its properties in the form's InitializeComponent function. The property values set in the form's designer are applied after the constructor to CustomControl has finished (which, if you think about it, they'd have to be).
Since you are setting your custom sizes in the control's constructor, they're probably getting overridden by the designer values immediately afterwards before the form is displayed.
A better place to set the size is the UserControl.Load event, which occurs after the designer properties have been set.
An even better option would be to properly support auto sizing.

Categories

Resources