I'm very new with VBA in Outlook.
So I'm trying to manipulate the Save Button for the Task Formulars.
I was wondering how to make a "different" Save Button wich actually does the same but with some extras.
So does anybody knows how you can save a Task that you're actually at with a VBA makro???
Thanks for Help
Bruno
You will need to create a new ribbon item that will be your custom button, this item should have your custom save function defined as its handler. Here is an example of how to customise the ribbon
namespace OutlookAddIn
{
[ComVisible(true)]
public class RibbonHook : Office.IRibbonExtensibility
{
private Office.IRibbonUI ribbon;
public RibbonHook()
{
}
public string GetCustomUI(string ribbonID)
{
string xml = string.Empty;
switch (ribbonID)
{
case "Microsoft.Outlook.Task":
xml = GetResourceText("OutlookAddIn.RibbonTask.xml");
break;
}
return xml;
}
#region Ribbon Callbacks
//Create callback methods here. For more information about adding callback methods, select the Ribbon XML item in Solution Explorer and then press F1
public void Ribbon_Load(Office.IRibbonUI ribbonUI)
{
this.ribbon = ribbonUI;
}
public void buttonTaskCustomSave_Action(Office.IRibbonControl control)
{
Outlook.Taskitem taskItem = (Outlook.taskItem)OutlookAddIn.Globals.OLA.Application.ActiveInspector().CurrentItem;
//do stuff with the task here
}
#endregion
#region Helpers
private static string GetResourceText(string resourceName)
{
Assembly asm = Assembly.GetExecutingAssembly();
string[] resourceNames = asm.GetManifestResourceNames();
for (int i = 0; i < resourceNames.Length; ++i)
{
if (string.Compare(resourceName, resourceNames[i], StringComparison.OrdinalIgnoreCase) == 0)
{
using (StreamReader resourceReader = new StreamReader(asm.GetManifestResourceStream(resourceNames[i])))
{
if (resourceReader != null)
{
return resourceReader.ReadToEnd();
}
}
}
}
return null;
}
#endregion
}
}
you will need to add this to your main addin class
protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
return new RibbonHook();
}
and this is the ribbon xml
<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="Ribbon_Load">
<ribbon>
<tabs>
<tab idMso="TabTask">
<group id="TaskCustom"
label="Custom Save">
<button id="buttonTaskCustomSave" label="Custom Save" onAction ="buttonTaskCustomSave_Action"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
Related
In my Plugin there is an action to open an Editor (extends EditorPart). When I try to open it a second time, its init method isn't called. Instead the focus is shifted to the editor that is already open.
The Editor is associated with a filetype. Here is the excerpt from the plugin.xml:
<extension point="org.eclipse.ui.editors">
<editor
class="de.blub.tool.ide.editors.GRASPEditor"
default="true"
extensions="grasp"
filenames="*.grasp"
icon="icons/newGraspFile.png"
id="de.blub.tool.ide.editors.GRASPEditor"
name="GRASP File Editor">
</editor>
</extension>
I have an Action to open a new Editor. When I try to click that Action twice it reuses the first Editor. I also tried to use an EditorMatcher that implements IEditorMatchingStrategy and always returns false in its matches() method. Even that doesn't change the behavior.
This seems to be a desired/default behavior in eclipse. How can I change that so that the user can initialize a new Editor each time?
Eclipse looks for the equals method of the IEditorInput instance. The Editor somewhere in its code (in my case in the doSave method) uses a setInput method like this:
#Override
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
// Initialize the editor input
this.input = new MyInputClass(resource);
...
}
#Override
public void doSave(IProgressMonitor monitor) {
...
setInput(input);
}
MyInputClass is the class that extends IEditorInput. The logic for eclipse to reuse an Editor or create a new one is in its equals method. The following example checks the path of an IResource field:
public class MyInputClass implements IEditorInput {
private IResource resource;
public MyInputClass(IResource resource) {
this.resource = resource;
}
public IResource getResource() {
return resource;
}
public void setResource(IResource resource) {
this.resource = resource;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj instanceof MyEditorClass) {
MyEditorClass other = (MyEditorClass) obj;
if (getResource().getFullPath().equals(other.getResource().getFullPath())) {
return true;
}
}
return false;
}
}
Of course one can define another logic inside the equals method. Make sure to not create a chaos, which is very well possible, as greg-449 pointed out in a comment.
I want to implement something like below in the Xamarin forms.
I don't see such implementation with Objective C so can't find a way to render it through Native either.
How do I implement this with Xamarin forms?
https://demo.mobiscroll.com/javascript/list/display#
Working Solution: what follows will get your two-column UIPickerView rendered from Xamarin.Forms, belatedly updated from my original post
Option 1: Custom Renderer
I used the documentation #Junior Jiang shared in comments to inspire a quick Custom Renderer that should suit your needs.
First, make a placeholder control in your shared/Xamarin.Forms project:
namespace samples.core.Controls
{
public class MultiPickerControl : Xamarin.Forms.StackLayout
{
public MultiPickerControl()
{
// Just a placeholder
}
}
}
Then, make the renderer in your iOS project:
[assembly: ExportRenderer(typeof(samples.core.Controls.MultiPickerControl), typeof(samples.iOS.Controls.Picker.MultiPickerRenderer))]
namespace samples.iOS.Controls.Picker
{
public class MultiPickerRenderer : ViewRenderer
{
static UIPickerView pickerControl;
static DemoModel pickerModel = new DemoModel(new UILabel());
public MultiPickerRenderer()
{
pickerControl = new UIPickerView(
new CGRect(
UIScreen.MainScreen.Bounds.X - UIScreen.MainScreen.Bounds.Width,
UIScreen.MainScreen.Bounds.Height - 230,
UIScreen.MainScreen.Bounds.Width,
180))
{
Model = pickerModel
};
}
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if(Control != null)
{
Control.Add(pickerControl);
}
if(e.NewElement != null)
{
(e.NewElement as StackLayout).Children.Add(pickerControl);
}
}
}
}
You'll also need a UIPickerViewModel to give to your picker. This example is derived from the documentation above:
public class DemoModel : UIPickerViewModel
{
public Dictionary<string, string[]> options = new Dictionary<string, string[]>()
{
{ "America", new string[] { "Mexico", "USA" } },
{ "Europe", new string[] { "Germany", "France", "Italy"} },
{ "Asia", new string[] { "Korea", "Japan"} },
};
public override nint GetComponentCount(UIPickerView pickerView) => 2; // This determines how many columns are rendered
public override nfloat GetComponentWidth(UIPickerView picker, nint component) => component == 0 ? 120f : 160f;
public override nfloat GetRowHeight(UIPickerView picker, nint component) => 40f;
/// <summary>
/// Determines the number of rows to render in a component
/// </summary>
public override nint GetRowsInComponent(UIPickerView pickerView, nint component)
{
if (component == 0)
return options.Keys.Count;
else
{
var driver = pickerView.SelectedRowInComponent(0);
return options.Values.ElementAt((int)driver).Length;
}
}
/// <summary>
/// Gets the display value for a row in a component
/// </summary>
public override string GetTitle(UIPickerView pickerView, nint row, nint component)
{
if (component == 0)
return options.Keys.ElementAt((int)row);
else
{
var driver = pickerView.SelectedRowInComponent(0);
return options.Values.ElementAt((int)driver).ElementAt((int)row);
}
}
[Export("pickerView:didSelectRow:inComponent:")]
public override void Selected(UIPickerView pickerView, nint row, nint component)
{
// Update the display for column 2 if the value of column 1 has changed
if (component == 0)
{
pickerView.ReloadComponent(1);
pickerView.Select(0, 1, false);
}
}
Lastly, reference your placeholder control in the markup (or code behind). Xamarin will resolve the platform implementation when your code is run.
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:samples.core.Controls;assembly=samples.core"
x:Class="samples.core.Views.EntryMoveNextView"
xmlns:ctrl="clr-namespace:samples.core.Controls;assembly=samples.core">
<ContentPage.Content>
<StackLayout Padding="15,25">
<controls:MultiPickerControl x:Name="MultiPicker"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
The result should look like this:
Be sure to hook into the events available
I've added the above example to a samples repo on my GitHub, if you'd like to browse as a unified source.
Option 2: Native View
You also might be able to accomplish this with a Native View (documentation). The gist would look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
x:Class="samples.core.Views.EntryMoveNextView"
xmlns:ctrl="clr-namespace:samples.core.Controls;assembly=samples.core">
<ContentPage.Content>
<StackLayout Padding="15,25">
<ios:UIPickerView>
<!-- Manipulate UIPickerView Properties Here -->
</ios:UIPickerView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
As with the linked documentation above, be sure to set GetComponentCount in your UIPickerViewModel to indicate how many columns you'll show.
Good luck!
I am very new to Xamarin Forms development and I need a popup dialog. I found exactly what I am looking for in https://github.com/rotorgames/Rg.Plugins.Popup, but I cannot for the life of me figure out how to use it. Could someone point me to a working example or provide some direction on use? The README.md on the site is not helping me much.
I want the the popup dialog to appear when a info button is clicked in the top navigation bar. All the popup needs is 1-2 buttons (and labels) for setting user settings.
This is for Xamarin.Forms: iOS and Android.
In simple steps:
Install the plugin in all the projects
Add the PopUp in your
Xaml
Use the methods they provide on the documentacion for Show/Hide the PopUp:
Task PushAsync(PopupPage page, bool animate = true)
Task PopAllAsync(bool animate = true)
They also provide a demo, check it:
https://github.com/rotorgames/Rg.Plugins.Popup/tree/master/src/Demo
Add a reference to the library, i.e. from nuget, to all projects.
Within your Android project, add this Rg.Plugins.Popup.Popup.Init(this, savedInstanceState); inside the MainActivity.cs OnCreate method, before Xamarin Forms Inits.
And the same for the iOS project, inside AppDelegate.cs FinishedLaunching method()
//Android
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Rg.Plugins.Popup.Popup.Init(this, savedInstanceState); /*Must add before the other Xamarin Inits*/
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
Xamarin.Forms.Forms.Init(this, savedInstanceState);
}
//iOS
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Rg.Plugins.Popup.Popup.Init(); /* place at the top*/
....
}
Add a new ContentPage (.xaml) to your Views directory.
<?xml version="1.0" encoding="utf-8" ?>
<pages:PopupPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
xmlns:animations="clr-namespace:Rg.Plugins.Popup.Animations; assembly=Rg.Plugins.Popup"
x:Class="MyProjectName.Views.MyContentPageName">
<pages:PopupPage.Animation>
<animations:ScaleAnimation
PositionIn="Center"
PositionOut="Center"
ScaleIn="1.2"
ScaleOut="0.8"
DurationIn="400"
DurationOut="300"
EasingIn="SinOut"
EasingOut="SinIn"
HasBackgroundAnimation="True"/>
</pages:PopupPage.Animation>
<StackLayout HorizontalAlignment="FillAndExpand" VerticalAlignment="FillAndExpand">
<!-- place your layout content here ....fx a close popup button -->
<Button Clicked="CloseBtn_Clicked" Text="Close" />
</StackLayout>
</pages:PopupPage>
In the ContentPage (PopupPage) code behind file, add using Rg.Plugins.Popup.Services; and inherit from the following
using Rg.Plugins.Popup.Services;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MyContentPageName: Rg.Plugins.Popup.Pages.PopupPage
{
public MyContentPageName()
{
InitializeComponent();
}
public void OnAnimationStarted(bool isPopAnimation)
{
// optional code here
}
public void OnAnimationFinished(bool isPopAnimation)
{
// optional code here
}
protected override bool OnBackButtonPressed()
{
// Return true if you don't want to close this popup page when a back button is pressed
return true;
}
// Invoked when background is clicked
protected override bool OnBackgroundClicked()
{
// Return false if you don't want to close this popup page when a background of the popup page is clicked
return false;
}
private async void CloseBtn_Clicked(object sender, EventArgs e)
{
await PopupNavigation.Instance.PopAsync(true);
}
}
From the .xaml.cs page, where you would like to open the popup, add this:
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Rg.Plugins.Popup.Contracts;
using Rg.Plugins.Popup.Services;
public partial class MyOtherPage : ContentPage
{
private IPopupNavigation _popup { get; set; }
private MyContentPageName _modalPage;
public MyOtherPage()
{
_popup = PopupNavigation.Instance;
_modalPage = new MyContentPageName();
}
protected override void OnAppearing()
{
base.OnAppearing();
_popup.Popped += Popup_Popped;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_popup.Popped -= Popup_Popped;
}
private async void Tapped_OpenModal(object sender, EventArgs e)
{
await _popup.PushAsync(_modalPage);
}
/// <summary> Triggered when the MyContentPageName popup is closed "PopAsync()" </summary>
private async void Popup_Popped(object sender, Rg.Plugins.Popup.Events.PopupNavigationEventArgs e)
{
/* add your logic here, if necessary */
}
}
*Note: If your modal simply displays static content, there is no need for a _popped event delegate within the OnAppearing()/OnDisappearing().
My requirement was to add a new menu entry to “Compare with” present in one of the 3rd party views.
As this was using “org.eclipse.ui.popupMenus” to add a menu entry to the above contribution. Even I was forced to use the same extension point even though its deprecated.
I was able to add a menu entry to the contribution with the below code
<extension point="org.eclipse.ui.popupMenus">
<objectContribution
adaptable="true"
id="test.id"
objectClass="local.change">
<action
class="compare.commparetool"
enablesFor="1"
id="id"
label="Compare "
menubarPath="compareWith/group1">
</action>
<visibility>
<objectState
name="local.change"
value=".txt">
</objectState>
</visibility>
</extension>
The above configuration is working fine.
Next requirement was to add property tester to hide the menu entries whenever a file selected is other than .txt file.
As we cannot add property tester to object contribution, I have used IAdapterfactory. Below code is not working.
Observation:
I have added many menu entries (“org.eclipse.ui.menus”) in different views in “Compare with” which is not related to this.
But if user clicks on any of these commands, and then try the view in question, it is working as expected as expected.
Below is the code. Am I missing anything. Do I need to register the adapters in some other place also??
<extension point="org.eclipse.core.runtime.adapters">
<factory
adaptableType="local.change"
class="LocalChangeAdapterFactory">
<adapter
type="org.eclipse.ui.IActionFilter">
</adapter>
</factory>
</extension>
public class LocalChangeAdapterFactory implements IAdapterFactory
{
#SuppressWarnings("unchecked")
#Override
public Object getAdapter(final Object adaptableObject, final Class adapterType)
{
if (adapterType == IActionFilter.class)
{
return LocalChangeActionFilter.getInstance();
}
return null;
}
#SuppressWarnings("unchecked")
#Override
public Class[] getAdapterList()
{
return new Class[] { LocalChangeActionFilter.class };
}
}
public class LocalChangeActionFilter implements IActionFilter
{
private static LocalChangeActionFilter INSTANCE = new LocalChangeActionFilter();
private LocalChangeActionFilter()
{
}
#Override
public boolean testAttribute(final Object target, final String name, final String value)
{
String fileName = "";
if(target.getId==1){
return true;
}else{
return false;
}
public static LocalChangeActionFilter getInstance()
{
return INSTANCE;
}
}
The adaptableType attribute of the adapter factory should specify the type of the existing object that you want to adapt to an IActionFilter. So this is probably a resource of file:
adaptableType="org.eclipse.core.resources.IResource">
The getAdapter method of the IActionFactory should return a class matching the adapter attribute, not your implementing class:
public Class[] getAdapterList()
{
return new Class[] { IActionFilter.class };
}
Your testAttribute method if the action filter must test the name parameter matches the value in the objectState:
#Override
public boolean testAttribute(final Object target, final String name, final String value)
{
if (name.equals("local.change"))
{
.... do test
return true;
}
return false;
}
I am trying to access settings in my config file, which is a series of xml elements listed as such:
<databases>
<database name="DatabaseOne" Value="[value]" />
<database name="DatabaseTwo" Value="[value]" />
</databases>
Now I want to access it. I have set up classes like so:
Public Class DatabaseConfigurationHandler
Inherits ConfigurationSection
<ConfigurationProperty("Databases", IsDefaultCollection:=True)> _
Public ReadOnly Property Databases() As DatabaseCollection
Get
Return CType(Me("Databases"), DatabaseCollection)
End Get
End Property
End Class
Public Class DatabaseCollection
Inherits ConfigurationElementCollection
Protected Overloads Overrides Function CreateNewElement() As ConfigurationElement
Return (New Database())
End Function
Protected Overloads Overrides Function GetElementKey(ByVal element As ConfigurationElement) As Object
Return (CType(element, Database).DatabaseName)
End Function
End Class
Public Class Database
Inherits ConfigurationElement
<ConfigurationProperty("name", IsKey:=True, IsRequired:=True)> _
Public Property DatabaseName() As String
Get
Return Me("name").ToString()
End Get
Set(ByVal Value As String)
Me("name") = Value
End Set
End Property
<ConfigurationProperty("value", IsRequired:=True)> _
Public Property DatabaseValue() As String
Get
Return Me("value").ToString()
End Get
Set(ByVal Value As String)
Me("value") = Value
End Set
End Property
End Class
I want to be able get the element by it's name and return the value but I can't see to do that:
Dim config As New DatabaseConfigurationHandler
config = System.Configuration.ConfigurationManager.GetSection("databases/database")
Return config.Databases("DatabaseOne")
Am I missing some code, what am I doing wrong? Any other errors in the above?
Thanks.
Here's a cut and paste from something very similar I did a few days ago.
Config:
<ListConfigurations>
<lists>
<add Name="blah" EndpointConfigurationName="blah" ListName="blah" ConnectionString="blah" TableName="blah" FieldsCsv="blah" DbFieldsCsv="blah"/>
<add Name="blah2" EndpointConfigurationName="blah" ListName="blah" ConnectionString="blah" TableName="blah" FieldsCsv="blah" DbFieldsCsv="blah"/>
</lists>
</ListConfigurations>
Config section C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
namespace App
{
/// <summary>
/// Individual list configuration
/// </summary>
class ListConfiguration : ConfigurationElement
{
[ConfigurationProperty("Name", IsKey = true, IsRequired = true)]
public string Name
{
get { return (string)this["Name"]; }
}
[ConfigurationProperty("EndpointConfigurationName", IsRequired = true)]
public string EndpointConfigurationName
{
get { return (string)this["EndpointConfigurationName"]; }
}
[ConfigurationProperty("ListName", IsRequired = true)]
public string ListName
{
get { return (string)this["ListName"]; }
}
[ConfigurationProperty("ConnectionString", IsRequired = true)]
public string ConnectionString
{
get { return (string)this["ConnectionString"]; }
}
[ConfigurationProperty("TableName", IsRequired = true)]
public string TableName
{
get { return (string)this["TableName"]; }
}
[ConfigurationProperty("FieldsCsv", IsRequired = true)]
public string FieldsCsv
{
get { return (string)this["FieldsCsv"]; }
}
[ConfigurationProperty("DbFieldsCsv", IsRequired = true)]
public string DbFieldsCsv
{
get { return (string)this["DbFieldsCsv"]; }
}
}
/// <summary>
/// Collection of list configs
/// </summary>
class ListConfigurationCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new ListConfiguration();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ListConfiguration)element).Name;
}
}
/// <summary>
/// Config section
/// </summary>
class ListConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("lists")]
public ListConfigurationCollection Lists
{
get { return (ListConfigurationCollection)this["lists"]; }
}
}
}
And the code to pick it up from the main app:
ListConfigurationSection configSection = null;
try
{
configSection = ConfigurationManager.GetSection("ListConfigurations") as ListConfigurationSection;
}
catch (System.Configuration.ConfigurationErrorsException)
{
}
There isn't any good reason to design this kind of stuff by hand anymore. Rather, you should be using the Configuration Section Designer on CodePlex:
http://csd.codeplex.com/
Once installed, you can just add a new item to your project (a configuration section designer) and then add the elements and the constraints. I've found it VERY easy to use, and I will probably never write a piece of code for configuration files again.
You can use this configuration handler instead.. It will work for ALL custom configuration sections
public class XmlConfigurator : IConfigurationSectionHandler
{
public object Create(object parent,
object configContext, XmlNode section)
{
if (section == null)
throw new ArgumentNullException("section",
"Invalid or missing configuration section " +
"provided to XmlConfigurator");
XPathNavigator xNav = section.CreateNavigator();
if (xNav == null)
throw new ApplicationException(
"Unable to create XPath Navigator");
Type sectionType = Type.GetType((string)
(xNav).Evaluate("string(#configType)"));
XmlSerializer xs = new XmlSerializer(sectionType);
return xs.Deserialize(new XmlNodeReader(section));
}
}
Your config file then has to include a reference to the type that the represents the root element
<ConnectionConfig
configType="MyNamespace.ConnectionConfig, MyNamespace.AssmblyName" >
A sample config file might look like this:
<?xml version="1.0" encoding="utf-8" ?>
<ConnectionConfig
configType="MyNamespace.ConnectionConfig, MyNamespace.AssmblyName" >
<ConnCompanys>
<ConnCompany companyName="CompanyNameHere">
<ConnApps>
<ConnApp applicationName="Athena" vendorName="Oracle" >
<ConnSpecs>
<ConnSpec environments="DEV"
serverName="Athena"
port="1521"
catalog="DatabaseName"
logon="MyUserName"
password="%%552355%8234^kNfllceHGp55X5g==" />
<!-- etc...
And you will need to define the classes that each xml element maps to... using the appropriate XmlSerialization attributes ...
[XmlRoot("ConnectionConfig")]
public class ConnectionConfig
{
private ConnCompanys comps;
[XmlArrayItem(ElementName = "ConnCompany")]
public ConnCompanys ConnCompanys
{
get { return comps; }
set { comps = value; }
}
public ConnApp this[string CompanyName, string AppName]
{ get { return ConnCompanys[CompanyName][AppName]; } }
public ConnSpec this[string CompanyName, string AppName, APPENV env]
{
get
{
return ConnCompanys[CompanyName][AppName, env];
}
}
}
public class ConnCompanys : List<ConnCompany>
{
public ConnCompany this[string companyName]
{
get
{
foreach (ConnCompany comp in this)
if (comp.CompanyName == companyName)
return comp;
return null;
}
}
public bool Contains(string companyName)
{
foreach (ConnCompany comp in this)
if (comp.CompanyName == companyName)
return true;
return false;
}
}
public class ConnCompany
{
#region private state fields
private string compNm;
private ConnApps apps;
#endregion private state fields
#region public properties
[XmlAttribute(DataType = "string", AttributeName = "companyName")]
public string CompanyName
{
get { return compNm; }
set { compNm = value; }
}
[XmlArrayItem(ElementName = "ConnApp")]
public ConnApps ConnApps
{
get { return apps; }
set { apps = value; }
}
#endregion public properties
#region indexers
public ConnApp this[string applicationName]
{ get { return ConnApps[applicationName]; } }
public ConnSpec this[string applicationName, APPENV environment]
{
get
{
foreach (ConnSpec con in this[applicationName].ConnSpecs)
if (con.Environment == environment)
return con;
return null;
}
}
#endregion indexers
}
etc...
My VB isn't up to much sorry but here's how you do it in C#.
C#
public class AppState : IConfigurationSectionHandler
{
static AppState()
{
xmlNode myConfigNode = (XmlNode)ConfigurationManager.GetSection("databases");
}
public object Create(object parent, object context, XmlNode configSection) {
return configSection;
}
}
App.Config
<configuration>
<configSections>
<section name="databases" type="MyAssembly.AppState, MyAssembly" />
</configSections>
<databases>
<database name="DatabaseOne" Value="[value]" />
<database name="DatabaseTwo" Value="[value]" />
</databases>
</configuration>
You might be interested in using a ConfigurationElementCollection where T is the type of the child ConfigurationElements. You can find sample code in C# at http://devpinoy.org/blogs/jakelite/archive/2009/01/10/iconfigurationsectionhandler-is-dead-long-live-iconfigurationsectionhandler.aspx
Cheers!
There's a nice simple way of doing this demonstrated here also:
codeproject.com/KB/XML/xml_config_section.aspx