DotNetNuke.Entities.Portals.PortalSettings.GetHostSettings() – Obsolete

Auf der Suche nach dem Clean Code meiner DotNetNuke Module werde ich in den nächsten Tagen über einige kleine Migrationsschritte berichten.

Beginnen werde ich in diesem Sinne mit der Ersetzung der bereits seit DotNetNuke Version 5.0 obsoleten Methode GetHostSettings aus dem Namespace DotNetNuke,Entities,Portals.PortalSettings.

Hier ein Code Auszug aus einem DNN Modul, welcher bis zur DotNetNuke Version 5.0 Verwendung so oder ähnlich eingesetzt wurde.

Der Code dient dazu, die in einem Portal erlaubten Dateiendungen zu ermitteln. Oder mit anderen Parametern einfach eine Einstellung auf Host Ebene auszulesen

string fileExtensions = DotNetNuke.Entities.Portals.PortalSettings.GetHostSettings()["FileExtensions"].ToString().ToLower();

Für die obsolete Methode kann man nun folgende beiden Varianten verwenden:


string fileExtensions = HostController.Instance.GetSettingsDictionary()["FileExtensions"].ToString().ToLower();

oder diese variante


string fileExtensions = HostController.Instance.GetString("FileExtensions").ToLower();

Ich persönlich finde die Variante mit GetString() in diesem Fall die angebrachtere.

DotNetNuke mit C# auf dem richtigen Weg, aber noch nicht am Ziel! (CTP-3)

Nachdem nun bereits die dritte CTP Version von DotNetNuke 6.0 verfügbar ist, ist es an der Zeit sich einmal den Source der C# Version genauer anzuschauen.

Vorab aber noch einmal

„Gratulation an DotNetNuke“

für die Entscheidung das Projekt von VB nach C# zu migrieren.

Das ist auf jeden Fall eine Zeitgemäße und Zukunftsweisende Entscheidung.

Und ebenfalls Gratulation für die im großen und ganzen gute Umsetzung der C# Migration, bis hierhin.

Nach dem herunterladen und dekomprimieren der Source Version, gelingt es beim ersten Versuch, die Solution in Visual Studio zu laden und anschließend zu erstellen.

Das war in der Vergangenheit nicht immer so (eher sogar selten).

Dennoch bin ich mehr als nur ein wenig enttäuscht, dass man im Zuge der Migration nicht gleich einen einheitlichen Stil für die Erstellung des Source Code (Design Guidelines) im Stile von StyleCop eingeführt und umgesetzt hat.

Auf jeden Fall sind solche Regeln im vorhandenen Source Code (noch) nicht durchgängig zu erkennen..

Schade finde ich auch die fehlende bzw. nur sehr spärlich vorhandene Kommentierung des Source Code.

Ich werde auf jeden Fall, jetzt wo DotNetNuke ein C# Projekt geworden ist, und somit in meiner bevorzugten Programmiersprache entwickelt wird, wieder ein wachsames Auge auf das Projekt werfen.

Meine eigenen DotNetNuke Module werden in naher Zukunft ebenfalls so überarbeiten, dass diese unter der neuen Version lauffähig sein werden.

Wer mehr über die aktuellen Beta und CTP Versionen erfahren möchte, kann dies unter dem nachfolgenden Link direkt auf http://www.dotnetnuke.com nachlesen.

Beta Informationen zu DotNetNuke

ContextMenuStrip – Dynamisch Separatoren anzeigen oder ausblenden (C#)

In diesem Artikel möchte ich eine Methode vorstellen mit welcher je nach sichtbaren Menüpunkten eines Kontextmenüs die Separatoren zwischen einzelnen Menügruppen angezeigt (visible == true) bzw ausgeblendet (visibel == false) werden.

Schauen wir uns doch zuerst einmal an um was es dabei wirklich geht.

Hier zuerst das Gesamte Menü mit allen Möglichen Menüpunkten.

Dieses Beispiel stellt das Kontextmenü einer Anwendung dar, die verschiedene Kalendereinträge enthalten kann.

Je nachdem welche Art Kalendereintrag (Planung, Anlieferung, Sperrzeit etc.) ausgewählt wurde und welche Berechtigungen ein Benutzer besitzt werden nur bestimmte Menüpunkte aus dieser Gesamtauswahl angezeigt.

Häufig wird das so geregelt, dass man anstelle die Menüpunkte auszublenden diese nur deaktiviert. In diesem Fall ist es dann nicht notwendig sich Gedanken darüber zu machen einzelne Separatoren für eventuell ganz wegfallende Menügruppen zu entfernen.

Nachfolgend nun einige Abbildungen die anzeigen was passiert wenn man die Menüpunkte ausblendet ohne sich um die Separatoren zu kümmern.

Man beachte die vielen direkt aufeinander folgenden Separatoren, Sieht nicht wirklich Professionell aus.

Nun kann man natürlich die Separatoren ebenfalls im Opening Event manuell Sichtbar oder Unsichtbar machen (setzen der Visible Eigenschaft), dass kann aber je nach Komplexität der Menüstruktur sehr schnell sehr mühsam und sehr fehleranfällig werden.

Genau aus diesem Grund habe ich mir hierzu eine Methode erstellt die sich um diese Aufgabe selbstständig kümmert und je nach sichtbaren Menüpunkten die benötigten Separatoren ebenfalls sichtbar oder unsichtbar macht.

Doch bevor ich die Methode zeige, möchte ich zuerst das Ergebnis anhand der nachfolgenden Abbildungen zeigen. Hier nun die korrekt dargestellten Menüs.

Das sieht doch schon viel besser aus !

So nun aber der Quellcode der Methode.

internal static void SetToolStripSeperatorForContextMenu(ContextMenuStrip contextMenuStrip)
{
	bool isVisible = false;
	int seperatorIndex = 0;
	for (int i = 0; i < contextMenuStrip.Items.Count; i++)
	{
		if (contextMenuStrip.Items[i] is ToolStripSeparator)
		{
			contextMenuStrip.Items[i].Visible = false;
		}

		if (contextMenuStrip.Items[i].Available)
		{
			if (seperatorIndex > 0)
			{
				contextMenuStrip.Items[seperatorIndex].Visible = true;
				seperatorIndex = 0;
			}
			isVisible = true;
		}
		if (contextMenuStrip.Items[i] is ToolStripSeparator)
		{
			if (isVisible)
			{
				seperatorIndex = i;
			}
			isVisible = false;
		}
	}
}

Übrigens die Methode einfach am Ende des Opening Event des Kontextmenüs wie folgt aufrufen.

SetToolStripSeperatorForContextMenu(((ContextMenuStrip) sender));

Und fertig.

Code Optimierung – Teil 1250 (in C# geht auch Spaghetti kodierung)

Ich bin zur Zeit, unter anderem damit beauftragt ein Projekt für einen Kunden zu überarbeiten und zu optimieren.

In diesem Zusammenhang bin ich Heute Morgen auf ein Stück Quellcode gestoßen der mich sofort an die früher (so vor 20 Jahren) sehr üblichen Spaghetti Code Programme erinnert hat.

Ich zeige hier einfach mal den Code dieser Methode wie ich sie vorgefunden habe und anschließend die von mir leicht überarbeitete Methode.

Die Methode wird übrigens dazu verwendet verschiedene Button ein und auszublenden, sowie in Abhängigkeit von Datenfeldern einer Datenbank den Button mit einem Image zu verschönern und entsprechende Datums und Uhrzeiten anzuzeigen oder auszublenden.
Ich denke anhand der nachfolgenden Abbildungen kann man sich ungefähr vorstellen was die Methode macht.

Hier der doch etwas unübersichtliche Code

#region Ankunft

if (entladung.AnkunftLKW.HasValue)
{
	dtpAnkunftDate.Visible = true;
	dtpAnkunftTime.Visible = true;
}
else
{
	dtpAnkunftDate.Visible = false;
	dtpAnkunftTime.Visible = false;
}
#endregion

#region Einfahrt

if (entladung.EinfahrtLKW.HasValue)
{
	dtpEinfahrtDate.Visible = true;
	dtpEinfahrtTime.Visible = true;
}
else
{
	dtpEinfahrtDate.Visible = false;
	dtpEinfahrtTime.Visible = false;
}

#endregion

#region Entladung Start

if (entladung.EntladeStart.HasValue)
{
	dtpEntladungStartDate.Visible = true;
	dtpEntladungStartTime.Visible = true;
	btnEntladungStart.ImageIndex = 0;
	if (entladung.EntladeEnde.HasValue)
	{
		btnEntladungStart.Enabled = false;
	}
	else
	{
		btnEntladungStart.Enabled = true;
	}
}
else
{
	dtpEntladungStartDate.Visible = false;
	dtpEntladungStartTime.Visible = false;
}

#endregion

#region Entlade Ende

if (entladung.EntladeEnde.HasValue)
{
	dtpEntladungEndeDate.Visible = true;
	dtpEntladungEndeTime.Visible = true;
	btnEntladungEnde.ImageIndex = 0;

	btnEntladungEnde.Enabled = !entladung.AusfahrtLKW.HasValue;
}
 else
{
	if (entladung.EntladeStart.HasValue)
	{
		btnEntladungEnde.Enabled = true;
	}
	else
	{
		btnEntladungEnde.Enabled = false;
		btnEntladungEnde.ImageIndex = -1;
	}

	dtpEntladungEndeDate.Visible = false;
	dtpEntladungEndeTime.Visible = false;

}

#endregion

#region Ausfahrt

if (entladung.AusfahrtLKW.HasValue)
{
	dtpAusfahrtDate.Visible = true;
	dtpAusfahrtTime.Visible = true;
}
else
{
	dtpAusfahrtDate.Visible = false;
	dtpAusfahrtTime.Visible = false;
}

#endregion

Hier der Optimierte Code

dtpAnkunftDate.Visible = entladung.AnkunftLKW.HasValue;
dtpAnkunftTime.Visible = entladung.AnkunftLKW.HasValue;

dtpEinfahrtDate.Visible = entladung.EinfahrtLKW.HasValue;
dtpEinfahrtTime.Visible = entladung.EinfahrtLKW.HasValue;

dtpEntladungStartDate.Visible = entladung.EntladeStart.HasValue;
dtpEntladungStartTime.Visible = entladung.EntladeStart.HasValue;

dtpEntladungEndeDate.Visible = entladung.EntladeEnde.HasValue;
dtpEntladungEndeTime.Visible = entladung.EntladeEnde.HasValue;

dtpAusfahrtDate.Visible = entladung.AusfahrtLKW.HasValue;
dtpAusfahrtTime.Visible = entladung.AusfahrtLKW.HasValue;

btnEntladungStart.ImageIndex = entladung.EntladeStart.HasValue ? 0 : -1;

btnEntladungStart.Enabled = ((!entladung.EntladeStart.HasValue) |
						((entladung.EntladeStart.HasValue && !entladung.EntladeEnde.HasValue)));

btnEntladungEnde.ImageIndex = entladung.EntladeEnde.HasValue ? 0 : -1;

btnEntladungEnde.Enabled = (entladung.EntladeStart.HasValue && !entladung.AusfahrtLKW.HasValue);

Man beachte die Zeilenzahlen. Die obere Variante hat genau die 4 fache Anzahl Code.
Obwohl, schön aussehen tun die vielen If’s und Else schon 🙂

Und da der untere Code viel kürzer und einfacher ist, braucht man auch nichts mehr mit Region umklammern.

Enum als Datasource für DataGridViewComboBoxColumn im DataGridView verwenden

Vor längerer Zeit habe ich in diesem Artikel berichtet wie man enum Werte über die DataSource Eigenschaft an eine Combobox binden kann.

Hier eine Darstellung einer solchen Bindung in einer normalen Windows Form:

Will man nun aber enum Werte in einem DataGridview an eine, als Combobox definierte, Spalte (DataGridViewComboBoxColumn) binden, geht das nicht ganz so einfach, und eben nicht so wie es in meinem oben erwähnten Artikel beschrieben ist.

Das Ergebnis soll so aussehen:

Wer schon einmal eine solche Tabelle programmiert hat, weiß, dass man nachdem man die DataSource Eigenschaft der DataGridViewComboBoxColumn zugewiesen hat, noch den ValueMember und den DisplayMember angeben muss (Siehe nachfolgende Abbildung).

Da wir die DataSource Eigenschaft bei der enum Variante über den Source Code zuweisen (es geht auch anders, dazu evtl. zu einem späteren Zeitpunkt mal mehr), müssen wir natürlich auch die beiden Eigenschaften  DisplayMember und ValueMember aus dem Quellcode heraus zuweisen.

Nur wie heißen die denn ?

Und genau hier liegt das Problem, bzw. die hier beschriebene Lösung.

Diese Eigenschaften gibt es bei der ursprünglich von mir beschriebenen Lösung nicht.

Und daher müssen wir uns solche Eigenschaften bilden. Hierzu verwenden ich eine Helper Klasse die das notwendige implementiert. (Siehe nachfolgenden Source Code)

Hier nun der Code der Klassen

[Serializable]
public class EnumHelper
{
	public int Key { get; set; }
	public string Value { get; set; }

	public static IEnumerable<ValueListItem> CreateDataSourceFromEnum(Type t)
	{
		var list = new List<ValueListItem>();

		Array aEnum = Enum.GetValues(t);
		foreach (var array in aEnum)
		{
			var vi = new ValueListItem
						 {
							 Key = (int) array,
							 Value = array.ToString()
						 };

			list.Add(vi);
		}
		return list;
	}

	public static IEnumerable<EnumHelper> DataSourceFromEnum(Type t)
	{
		var list = new List<EnumHelper>();

		Array aEnum = Enum.GetValues(t);
		foreach (var array in aEnum)
		{
			var vi = new EnumHelper
						 {
							 Key = (int) array,
							 Value = array.ToString()
						 };

			list.Add(vi);
		}
		return list;
	}

}

[Serializable]
public class ValueListItem
{
	public int Key { get; set; }
	public string Value { get; set; }
}

Und so kann man die Helper Klasse verwenden um die DataSource Eigenschaft sowie die ValueMember und DisplayMember Eigenschaften zuzuweisen.

private void InitDgv()
{
	bereichDataGridViewTextBoxColumn.DataSource = EnumHelper.DataSourceFromEnum(typeof(EntladungsTyp));
	bereichDataGridViewTextBoxColumn.DisplayMember = "Value";
	bereichDataGridViewTextBoxColumn.ValueMember = "Key";
}

Wichtiger Hinweis

Ich verwende diese Klasse bereits seit mehreren Jahren und weiß nicht mehr genau ob ich von irgend jemand etwas übernommen und überarbeitet habe, oder ob ich das im stillen Kämmerchen ausgebrütet habe.
Eine kurze Recherche im Internet hat keine direkten Ergebnisse / Quellen hervorgebracht. Es gibt zwar einige Beiträge die sich auch mit enum Helper Klassen beschäftigen, aber die, welche ich gerade gesehen habe, beschäftigen sich nicht genau mit dem von mir hier beschriebenen Thema, so dass ich zur Zeit keine Angaben über eventuelle Quellen, die mich inspiriert haben könnten, machen kann.

Aber wenn jemand erkennt dass ich Code von wem auch immer verwendet habe, hinterlasst dass bitte in einem Kommentar, so dass sich eventuell fehlende Quellnachweise schließen.

DotNetNuke setzt in Zukunft voll auf C#

Laut einer Meldung der DotNetNuke Corporation wird DotNetNuke in Zukunft auch im Core und Core Module in C# entwickelt anstelle in VB.

Seit Jahren hat sich DotNetNuke Corp. vehement gegen eine Umstellung von VB zu C# als Entwicklungssprache gewehrt. Ich selbst habe während meiner Zeit als Projektleiter eines DotNetNuke Core Moduls (Blog Module) etliche Diskussionen mit den US Boys über das Thema geführt.

Bereits im Jahr 2005 habe ich angefangen DotNetNuke Module in C# zu entwickeln, anfangs noch von vielen belächelt, aber im Laufe der Jahre mit immer mehr Selbstverständlichkeit angenommen und später vielfach auch von anderen als Selbstverständlich übernommen.
Heute gibt es eine Fülle von DNN Modulen die bereits in C# programmiert sind.

Laut der Meldung soll die Umstellung des Core Framework mit DotNetNuke 6.0 vollzogen werden.

Das erste Core Modul „Q&A“ soll sich ebenfalls bereits in der Umstellung befinden.

Da ich selbst im Laufe meines Entwicklerlaufbahn einige Projekte von VB auf C# umgestellt (nicht nur DNN Module) und weiß daher, dass dies nicht einfach mit einem Generator 1:1 gemacht werden kann. Schon gar nicht wenn man lesbaren Code erhalten möchte. Daher stellt sich die Frage, ob DotNetNuke 6.0 nahe an einer SharpNetNuke Version 1.0 sein wird. Damit meine ich nicht den Funktionsumfang, sondern die Fehlerträchtigkeit.

Ich hoffe, dass dies nicht geschehen wird.

Übrigens soll die Version 6.0 irgendwann im 2. Quartal 2011 verfügbar gemacht werden.

Ich freue mich jedenfalls drauf, dann macht es auch wieder mehr Spaß am und im den Core Quellcode herum zu programmieren.

C# -Die Auflistung wurde geändert – Der Enumerationsvorgang kann möglicherweise nicht ausgeführt werden

Eine Meldung ähnlich wie die Überschrift kann man ganz leicht erhalten, wenn man mit Auflistungen in einer Anwendung arbeitet, welche mehrere Threads verwendet und ein Thread lesend und ein anderer schreiben auf die Auflistung zugreift.

Nun ist es so, dass es zu diesem Thema eine ganze Reihe, von mehr oder weniger, guten Ansätzen im Internet zu finden ist (Natürlich überwiegend gute 🙂 ). Wobei eine der entscheidenden Aussagen ist, dass man diese Fehlermeldungen, dann erhält wenn man mit einer Enumeration (foreach Schleife) über die Auflistung geht und dabei neue Elemente der Auflistung hinzufügen oder Elemente löschen möchte.

Also zum Beispiel wie in der folgenden Klasse:

public class AppointmentList : List<Appointment>
{
	public void DeleteAppointmentsForDate(DateTime dt)
	{
		foreach (Appointment appointment in this)
		{
			if (appointment.StartDate.Date == dt.Date)
			{
				Remove(appointment);
			}
		}
	}
}

Eigentlich soll, die Methode der Klasse alle Appointments eines bestimmten Datums aus der Auflistung löschen.
Das mach sie auch, und zwar dann, wenn alles in einem Thread abläuft.

Aber nur dann.

Nun wird bei den meisten Lösungen im Internet vorgeschlagen anstelle der Iteration mit foreach einfach eine for Schleife zu verwenden.

Das würde dann so aussehen
(Ich möchte aber gleich hier sagen, dass diese Lösung für das Löschen auch nicht eingesetzt werden kann, da diese egal ob mit oder ohne mehreren Threads nicht richtig arbeitet).

public class AppointmentList : List<Appointment>
{
		for (int i = 0; i < this.Count; i++)
		{
			if (this[i].StartDate.Date == dt.Date)
			{
				this.RemoveAt(i);
			}
		}
	}
}

Und warum geht das nicht ?

Nehmen wir der Einfachheit halber mal an die Auflistung würde 4 Einträge enthalten und alle 4 würden das gleiche Startdatum haben, was bedeuten würde, dass alle 4 Listen Elemente gelöscht werden müssten.

Runde 1:
i == 0 und this.count== 3
Element an Index 0 wird gelöscht
Element mit Index 1 wird nun an Index Position 0 geführt
Element mit Index 2 wird nun an Index Position 1 geführt
Element mit Index 3 wird nun an Index Position 2 geführt
this.count ist nun nur noch 2

Runde 2:
i == 1 und this.count == 2
….

Eigentlich brauche ich gar nicht mehr weiter erläutern, jetzt ist klar, dass das löschen mit der for schleife mit erhöhendem Index (i++) nicht funktionieren kann, wenigstens nicht wenn es um das Löschen von List Elementen geht.

Aber natürlich gibt es auch dafür eine einfache Lösung, und diese sieht so aus:

public class AppointmentList : List<Appointment>
{
	public void DeleteAppointmentsForDate(DateTime dt)
	{
            for (int i = this.Count - 1; i >= 0; i--)
            {
                if (this[i].StartDate.Date == dt.Date)
                {
                    this.RemoveAt(i);
                }
            }
		}
	}
}

Genau, wir verwenden einfach eine for schleife mit einem herabzählendem Index (i–).
Dies funktioniert auch mit mehreren Threads.

C# – Dauer in Minuten aus Start und Ende Zeit ermitteln

Und wieder mal geht es um Zeitberechnung in C#.

Aufgabenstellung:
Wir haben 2 Datumswerte mit Zeitangabe. Nenne wir den ersten den Starttermin und den zweiten den Endetermin.
Aus diesen beiden Werten wollen wir nun die Dauer des Termins in Minuten ermitteln.

Und hier die Lösung:

DateTime _dtStart = DateTime.Now;
DateTime _dtEnde = _dtStart.AddHours(1); // Zum Test 1 Stunde addieren, das macht dann  60 Minuten Dauer

int dauer = (int) (_dtEnde - _dtStart).TotalMinutes;

Übrigens geht das natürlich auch wenn man anstelle einer DateTime Variable einen bzw. zwei DateTimePicker Werte verwenden möchte mann muss dann lediglich die Eigenschaft Value des DateTimePicker verwenden, also so:

            DateTimePicker dtpStart = new DateTimePicker();
            DateTimePicker dtpEnde = new DateTimePicker();
            dtpStart.Value = DateTime.Now;
            dtpEnde.Value = dtStart.Value.AddHours(1);

            int dauer = (int)(dtpEnde.Value-dtpStart.Value).TotalMinutes;

c# – Splitt Container Collapse Panel Advanced Example – WinForm

In einem aktuellen Projekt hat sich der Kunde gewünscht, ein „platzsparendes“ Winform, zur Anzeige von Kalenderinformationen, zu erhalten. Und da der Wunsch des Kunden gleich einem Befehl ist…. 🙂 Voila !

Hier (m)ein Lösungsansatz.

Und da ich denke, dass auch im Zeitalter von WPF, noch immer WinForm Anwendungen erstellt werden, veröffentliche ich diesen Lösungsansatz (der übrigens schon in einigen Projekten eingesetzt wird und nicht ganz neu ist) hier in diesem Beitrag und hoffe dass dies dem einen oder anderen auch Heute noch dabei behilflich sein kann, ein ähnliches Problem so einfacher lösen zu können.
Wer das Beispiel 1 : 1 in einem eigenen Projekt verwenden möchte:

Bitte schön !!!

Hier kurz noch einmal die Aufgabenstellung:
Die Form soll auf der linken Seite ein Calendar Month Control enthalten und auf der rechten Seite ein TabControl enthalten, welches dann ein komplexes Calendar Control (ähnlich wie Outlook Kalender) enthält.
Und genau da kommt wohl auch der Wunsch her, dass man das Calendar Month Control ausblenden und einblenden kann.

Eigentlich ….
gar nicht so aufwendig, aber ….
Wenn man das ganze umsetzen will, stößt man doch recht schnell an die „einfachen“ Grenzen der verfügbaren Controls.

Zur Demonstration, dass es doch nicht so aufwendig ist, habe ich dieses kleine Beispiel erstellt, welches man auch als VS2010 Projekt am Ende des Beitrags herunterladen kann.

Außer, dass man, die benötigten Controls in der richtigen Reihenfolge auf das Form ziehen muss (Siehe im Source Code) benötigt man nur ein paar Zeilen, den ich nachfolgend Kurz zeigen möchte.

namespace WinFormSplittContainerAdvancedExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            splitContainerRight.Panel1Collapsed = true;
        }

        private void btnHideLeftPanel_Click(object sender, EventArgs e)
        {
            splitContainerMain.Panel1Collapsed = true;
            panelShow.Visible = true;
            splitContainerRight.Panel1Collapsed = false;
        }

        private void btnShowLeftPanel_Click(object sender, EventArgs e)
        {
            splitContainerMain.Panel1Collapsed = false;
            panelShow.Visible = false;
            splitContainerRight.Panel1Collapsed = true;
        }
    }
}

Und so sieht das ganze im laufenden Betrieb aus:

Hier der Screenshot mit angezeigtem linken Kalender Panel

Und hier der Screenshot mit ausgeblendetem linken Kalender Panel

Und hier der Download: WinFormSplittContainerAdvancedExample (Einfach ZIP File entpacken und mit Visual Studio 2010 öffenen.

Viel Spaß!!

c# – tabControl – tabPage – Fehlende Visible Eigenschaft

Wer in WinForms Anwendungen das tabControl einsetzt wird über kurz oder lang darüber stolpern, dass die tabPage Controls keine Visible Eigenschaft besitzen.

Das bedeute es ist weder in der Entwicklungsumgebung noch zur Laufzeit Möglich einfach durch setzen der Visible Eigenschaft einzelne tabPage Seiten zwischen sichtbar und unsichtbar umzuschalten.

Hier daher ein kleines Code Snippet mit dem man die tabPage aus dem Programmcode heraus aus der tabPage Auflistung des tabControls entfernen oder hinzufügen kann.

// Hier der Code für Visible=False
tabControlOptionen.TabPages.Remove(tabUser);

// Hier der Code für Visible=True
tabControlOptionen.TabPages.Add(tabUser);

TabPageVisibleHideDemo