When a MediaElement switches to FullWindow mode, the internal FullWindowMediaRoot becomes its temporary host. When visible, FullWindowMediaRoot sits on top of the normal RootScrollViewer, i.e. it is displayed as an overlay over the current page, which is the expected behaviour.
My problem was with the BottomAppBar. Its host is the internal PopupRoot, which unfortunately sits on top of the FullWindowMediaRoot. So when I use a BottomAppBar on a page that allows a user to switch a MediaElement to FullWindow, the user cannot use the MediaElement's controls since these elements are almost completely hidden by the still visible BottomAppBar.
I have pulled my hair out over this problem for nearly a day and have found a solution that works for me. If someone has a better answer, I would be grateful for sharing. Until then, I am documenting my current working solution below for anyone running into the same issue.
My solution uses a class implementing IValueConverter, which raises an event whenever the IsFullWindow property of a MediaElement changes value.
The page initialiser adds an event handler to the converter's FullWindowStateChanged event:
this.isFullWindowConverter.FullWindowStateChanged += this.OnFullWindowStateChanged;
and handles it like this:
/// <summary>
/// Handles the FullWindowStateChanged event of IsFullWindowConverter by adjusting the visibility of the BottomAppBar to the full window
/// state of this page's MediaElement.
/// </summary>
/// <param name="sender">The instance of IsFullWindowConverter raising the event.</param>
/// <param name="e">The parameter is not used.</param>
private void OnFullWindowStateChanged(object sender, EventArgs e)
{
this.BottomAppBar.Visibility = ((IsFullWindowConverter)sender).IsFullWindow ? Visibility.Collapsed : Visibility.Visible;
}
Here the converter class:
namespace Filmit.Win
{
using System;
using Windows.UI.Xaml.Data;
/// <summary>
/// This converter raises an event when the IsFullWindow property of a MediaElement has changed. The converter is added to the resources of the page that hosts the MediaElement:
/// <code><local:IsFullWindowConverter x:Key="isFullWindowConverter" x:Name="isFullWindowConverter"/></code>
/// This converter resource is then bound to the IsFullWindow property of a MediaElement, solely for the event raising effects of the ConvertBack method this converter.
/// The actual value of the IsFullWindow property is returned as is.
/// <code><MediaElement IsFullWindow="{Binding RelativeSource={RelativeSource Self}, Path=IsFullWindow, Converter={StaticResource isFullWindowConverter}, Mode=TwoWay}"></code>
/// The subscriber to the FullWindowStateChanged event checks the IsFullWindow property to get the current full window state of the MediaElement.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1603:DocumentationMustContainValidXml", Justification = "XAML Page.Resources declaration.")]
public class IsFullWindowConverter : IValueConverter
{
/// <summary>
/// Raises the FullWindowStateChanged event. Subscribers check the IsFullWindow property to get the current full windows state.
/// </summary>
public event EventHandler<EventArgs> FullWindowStateChanged = null;
/// <summary>
/// Gets a value indicating whether the mode of the MediaElement is FullWindow.
/// </summary>
public bool IsFullWindow { get; private set; }
/// <summary>
/// Required implementation of IValueConverter.Convert, returning the passed in boolean object as a bool.
/// </summary>
/// <param name="value">The boolean object.</param>
/// <param name="targetType">The expected target type.</param>
/// <param name="parameter">The parameter is not used.</param>
/// <param name="language">The parameter is not used.</param>
/// <returns>The passed in boolean object as a bool.</returns>
public object Convert(object value, Type targetType, object parameter, string language)
{
bool? isFullWindow = value as bool?;
this.IsFullWindow = isFullWindow.HasValue ? isFullWindow.Value : false;
return this.IsFullWindow;
}
/// <summary>
/// This implementation of IValueConverter.ConvertBack is called when the IsFullWindow property of the MediaElement has changed its value.
/// It raises the FullWindowStateChanged event and returns the passed in boolean object as a bool.
/// </summary>
/// <param name="value">The boolean object.</param>
/// <param name="targetType">The expected target type.</param>
/// <param name="parameter">The parameter is not used.</param>
/// <param name="language">The parameter is not used.</param>
/// <returns>The passed in boolean object as a bool.</returns>
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
bool? isFullWindow = value as bool?;
this.IsFullWindow = isFullWindow.HasValue ? isFullWindow.Value : false;
if (this.FullWindowStateChanged != null)
{
this.FullWindowStateChanged(this, new EventArgs());
}
return this.IsFullWindow;
}
}
}
If you are creating a UWP app, I think you can put a AppBar in the Grid, and locate it to the bottom of the page. And this will solve your porblem too. But if you are working on a windows 8.1 app, this method will not work.
XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<MediaElement x:Name="MyMedia" Source="Assets/Sofia Jannok-Liekkas.wma" AreTransportControlsEnabled="True" />
<AppBar VerticalAlignment="Bottom">
<CommandBar>
<CommandBar.Content>
<Grid/>
</CommandBar.Content>
<AppBarButton Icon="Accept" Label="appbarbutton"/>
<AppBarButton Icon="Cancel" Label="appbarbutton"/>
<AppBarButton Content="Maximize" VerticalAlignment="Center" HorizontalAlignment="Center" Click="maximize"/>
</CommandBar>
</AppBar>
</Grid>
C#:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public void maximize(object sender, RoutedEventArgs e)
{
MyMedia.IsFullWindow = !MyMedia.IsFullWindow;
}
}
Any way, your solution will solve the problem. Thank you for sharing.
Related
Per this Using System.Text.Json to Serialize an IConfiguration back to Json it seems that the limited type information that you can put into JSON is discarded.
You seem to be under the impression that IConfiguration objects are storing ints, bools, etc. (for example) corresponding to the JSON Element type. This is incorrect. All data within an IConfiguration is stored in stringified form. The base Configuration Provider classes all expect an IDictionary<string, string> filled with data. Even the JSON Configuration Providers perform an explicit ToString on the values.
I noticed this when I addressed the problem posed in that question with this extension method.
using System.Collections.Generic;
using System.Dynamic;
using Microsoft.Extensions.Configuration;
public static class ExtendConfig
{
public static dynamic AsDynamic(this IConfigurationRoot cr)
{
var result = new ExpandoObject();
var resultAsDict = result as IDictionary<string, object>;
foreach (var item in cr.AsEnumerable())
{
resultAsDict.Add(item.Key, item.Value);
}
return result;
}
}
This method reconstructs the graph but everything is now a string.
I could write my own parser and apply it to the original JSON string but that's a bit dire. Is there any way I can get this metadata so I can improve the fidelity of merged configs? I'm passing it through for consumption by JS which does notice the difference.
Merging is why I'm using the config extensions builder.
As IConfiguration doesn't provide the information about types, but System.Text.Json used by JsonConfigurationProvider does, the working solution (or a workaround) would be using the System.Text.Json deserializer directly to read the configuration file and match the types to the configuration keys.
But we have some minor issues to solve first. Like - where is the configuration file? We don't want to duplicate that information in code, we have to extract it from IConfiguration instance.
Then - match the concrete existing configuration key to the JSON document tree node. That will require either DFS or BFS tree traversal algorithm. I'll go for DFS (Depth First Search). In a nutshell - if you have expandable nodes, you put them in the stack in reverse order. Then you have a while loop that takes a node from the stack, if it has children, you put them on the same stack, if not - you just yield the node. As simple as that, and BFS is pretty similar, but nevermind.
There's one thing more: Newtonsoft.Json - a popular Nuget package that was at a time used even by Microsoft. That JSON serializer is a little slower than System.Text.Json, but it's more advanced, allowing the user to build a JSON document tree node by node.
Having this powerful tool makes creating a writeable JSON IConfiguration relatively easy, especially using some helpers like mine below.
Look at the SaveChanges() method. It walks through the IConfiguration nodes, matches appropriate JObject nodes by their path and copies the changes from IConfiguration instance to the JObject instance. Then you can just write the JSON file.
There's an ugly hack used to get the file. I get the private field containing the IConfigurationRoot instance, but that could be skipped if you already have the configuration root. Having the root you can get JsonConfigurationProvider from it, then it's just Source.Path property.
So here's the code. It's a part of the Woof.Toolkit and Woof.Config Nuget package, that provides writeable JSON configurations, some helper methods to them, and also Azure Key Vault client that uses a JSON configuration, with some helper methods to encrypt and decrypt sensitive data with keys stored on AKV.
This is the first release of ConfigurationExtensions class, so it might be sub-optimal in performance terms, but it works and illustrates how you can match the IConfiguration instance nodes with JObject nodes to get the types of the configuration properties.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Newtonsoft.Json.Linq;
namespace Woof.Config;
/// <summary>
/// Extensions for <see cref="IConfiguration"/> making JSON type <see cref="IConfiguration"/> writeable.
/// </summary>
public static class ConfigurationExtensions {
/// <summary>
/// Gets the configuration root element.
/// </summary>
/// <param name="configuration">Any <see cref="IConfiguration"/> part.</param>
/// <returns>Root element.</returns>
public static IConfigurationRoot? GetRoot(this IConfiguration configuration) {
if (configuration is IConfigurationRoot root) return root;
var rootField = configuration.GetType().GetField("_root", BindingFlags.Instance | BindingFlags.NonPublic);
return rootField?.GetValue(configuration) as IConfigurationRoot;
}
/// <summary>
/// Gets the first <see cref="JsonConfigurationProvider"/> if exists, null otherwise.
/// </summary>
/// <param name="root">Configuration root element.</param>
/// <returns><see cref="JsonConfigurationProvider"/> or null.</returns>
public static JsonConfigurationProvider? GetJsonConfigurationProvider(this IConfigurationRoot root)
=> root.Providers.OfType<JsonConfigurationProvider>().FirstOrDefault();
/// <summary>
/// Gets the first <see cref="JsonConfigurationProvider"/> if exists, null otherwise.
/// </summary>
/// <param name="config">Any <see cref="IConfiguration"/> part.</param>
/// <returns><see cref="JsonConfigurationProvider"/> or null.</returns>
public static JsonConfigurationProvider? GetJsonConfigurationProvider(this IConfiguration config)
=> config.GetRoot()?.GetJsonConfigurationProvider();
/// <summary>
/// Saves changes made to <see cref="IConfiguration"/> to the JSON file if exists.
/// </summary>
/// <param name="config">Any <see cref="IConfiguration"/> part.</param>
/// <exception cref="InvalidOperationException">Configuration does not have <see cref="JsonConfigurationProvider"/>.</exception>
public static void SaveChanges(this IConfiguration config) {
var provider = config.GetJsonConfigurationProvider();
if (provider is null) throw new InvalidOperationException("Can't get JsonConfigurationProvider");
var sourceJson = File.ReadAllText(provider.Source.Path);
var target = JObject.Parse(sourceJson);
var stack = new Stack<IConfigurationSection>();
foreach (IConfigurationSection section in config.GetChildren().Reverse()) stack.Push(section);
while (stack.TryPop(out var node)) {
var children = node.GetChildren();
if (children.Any()) foreach (var child in children.Reverse()) stack.Push(child);
else {
var jPath = GetJPath(node.Path);
var element = target.SelectToken(jPath);
var valueString =
element!.Type == JTokenType.Null
? "null" :
element!.Type == JTokenType.String ? $"\"{node.Value}\"" : node.Value;
element!.Replace(JToken.Parse(valueString));
}
}
File.WriteAllText(provider.Source.Path, target.ToString());
}
/// <summary>
/// Sets <paramref name="configuration"/>'s <paramref name="key"/> with specified <paramref name="value"/>.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="key">The key of the configuration section.</param>
/// <param name="value">Value to set.</param>
/// <exception cref="InvalidOperationException">Not supported type as value.</exception>
public static void SetValue(this IConfiguration configuration, string key, object? value) {
var c = CultureInfo.InvariantCulture;
var valueString = value switch {
null => null,
string v => v,
Uri v => v.ToString(),
byte[] v => Convert.ToBase64String(v),
bool v => v.ToString(c),
int v => v.ToString(c),
decimal v => v.ToString(c),
double v => v.ToString(c),
uint v => v.ToString(c),
long v => v.ToString(c),
ulong v => v.ToString(c),
short v => v.ToString(c),
ushort v => v.ToString(c),
byte v => v.ToString(c),
sbyte v => v.ToString(c),
float v => v.ToString(c),
_ => throw new InvalidOperationException($"Cannot set value of type {value.GetType()}")
};
configuration[key] = valueString;
}
/// <summary>
/// Gets the path for JObject.SelectToken method.
/// </summary>
/// <param name="path"><see cref="IConfiguration"/> path.</param>
/// <returns><see cref="JObject"/> path.</returns>
private static string GetJPath(string path) => RxIConfigurationIndex.Replace(path, "[$1]").Replace(':', '.');
/// <summary>
/// Matches the <see cref="IConfiguration"/> indices.
/// </summary>
private static readonly Regex RxIConfigurationIndex = new(#":(\d+)", RegexOptions.Compiled);
}
Why JObject? Can JSON file represent only an object? No - it can represent any value, including null. But the JSON configuration MUST be an object. That's why I use JObject as my secondary configuration root.
unfortunately, I was not successful with my research for this topic.
With an anchor tag, I was able to do this:
My Link ®
Now I want the same with an Html.Actionlink:
#Html.ActionLink("My Link ®", "Action")
But the output is the same as the input and not a reg symbol as it is intended.
Any idea?
Thanks in advance!
#Html.ActionLink("My Link ®", "Action")
or
My Link ®
ActionLink always use call of HttpUtility.Encode for the link text.
You can use UrlHelper Method like
My Link ®
You can use an HtmlString (MvcHtmlString in .NET 2 / MVC 2) to indicate that you do not wish it to be re-encoded:
#Html.ActionLink(new HtmlString("My Link ®"), "Action");
Here is how I solved this in MVC 2:
/// <summary>
/// Creates an anchor tag based on the passed in controller type and method.
/// Does NOT encode passed in link text.
/// </summary>
/// <typeparam name="TController">The controller type</typeparam>
/// <param name="htmlHelper">The HTML helper</param>
/// <param name="action">The method to route to</param>
/// <param name="linkText">The linked text to appear on the page</param>
/// <returns>A formatted anchor tag</returns>
public static MvcHtmlString ActionLink<TController>( this HtmlHelper htmlHelper,
Expression<Action<TController>> action,
HtmlString linkText ) where TController : Controller
{
return ActionLink( htmlHelper, action, linkText, null, null );
}
/// <summary>
/// Creates an anchor tag based on the passed in controller type and method.
/// Does NOT encode passed in link text.
/// </summary>
/// <typeparam name="TController">The controller type</typeparam>
/// <param name="htmlHelper">The HTML helper</param>
/// <param name="action">The method to route to</param>
/// <param name="linkText">The linked text to appear on the page</param>
/// <param name="routeValues">The route values</param>
/// <param name="htmlAttributes">The HTML attributes</param>
/// <returns>A formatted anchor tag</returns>
public static MvcHtmlString ActionLink<TController>( this HtmlHelper htmlHelper,
Expression<Action<TController>> action,
HtmlString linkText,
object routeValues,
object htmlAttributes ) where TController : Controller
{
var routingValues = GetRouteValuesFromExpression( action, routeValues );
var url = UrlHelper.GenerateUrl( null, //routeName
null, //actionName
null, //controllerName
routingValues,
htmlHelper.RouteCollection,
htmlHelper.ViewContext.RequestContext,
false ); //includeImplicitMvcValues
var tagBuilder = new TagBuilder("a")
{
InnerHtml = !String.IsNullOrEmpty( linkText.ToString() ) ? linkText.ToString() : String.Empty
};
tagBuilder.MergeAttributes( (IDictionary<string, object>)htmlAttributes );
tagBuilder.MergeAttribute( "href", url );
return MvcHtmlString.Create( tagBuilder.ToString( TagRenderMode.Normal ) );
}
It is strongly typed, as in the MVC futures NuGet package. So you can use it like this:
<%= Html.ActionLink<HomeController>( x => x.Index(),
new HtmlString( "Don't Encode Me!<sup>®</sup>" ) ) %>
Having a horrible time reading a custom configuration section from web.config:
I am using Configuration Section Designer (http://csd.codeplex.com/).
UPDATE:
here is the error I am getting:
System.Configuration.ConfigurationErrorsException: An error occurred creating the configuration section handler for SendToTestConfig/sendToTestIndexConfig: No parameterless constructor defined for this object. (C:\TFS\Mainline\Business.Utility.SendToTest\Business.Utility.SendToTest\web.config line 20) ---> System.MissingMethodException: No parameterless constructor defined for this object.
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandle& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean fillCache)
at System.RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache)
Here is my auto-generated configuration section:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.225
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Business.Utility.SendToTest.Configuration
{
/// <summary>
/// The SendToTestIndexConfig Configuration Section.
/// </summary>
public partial class SendToTestIndexConfig : global::System.Configuration.ConfigurationSection
{
#region Singleton Instance
/// <summary>
/// The XML name of the SendToTestIndexConfig Configuration Section.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
internal const string SendToTestIndexConfigSectionName = "sendToTestIndexConfig";
/// <summary>
/// Gets the SendToTestIndexConfig instance.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
public static global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig Instance
{
get
{
return ((global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig)(global::System.Configuration.ConfigurationManager.GetSection(global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.SendToTestIndexConfigSectionName)));
}
}
#endregion
#region Xmlns Property
/// <summary>
/// The XML name of the <see cref="Xmlns"/> property.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
internal const string XmlnsPropertyName = "xmlns";
/// <summary>
/// Gets the XML namespace of this Configuration Section.
/// </summary>
/// <remarks>
/// This property makes sure that if the configuration file contains the XML namespace,
/// the parser doesn't throw an exception because it encounters the unknown "xmlns" attribute.
/// </remarks>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
[global::System.Configuration.ConfigurationPropertyAttribute(global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.XmlnsPropertyName, IsRequired=false, IsKey=false, IsDefaultCollection=false)]
public string Xmlns
{
get
{
return ((string)(base[global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.XmlnsPropertyName]));
}
}
#endregion
#region IsReadOnly override
/// <summary>
/// Gets a value indicating whether the element is read-only.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
public override bool IsReadOnly()
{
return false;
}
#endregion
#region appGroups Property
/// <summary>
/// The XML name of the <see cref="appGroups"/> property.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
internal const string appGroupsPropertyName = "appGroups";
/// <summary>
/// Gets or sets the appGroups.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
[global::System.ComponentModel.DescriptionAttribute("The appGroups.")]
[global::System.Configuration.ConfigurationPropertyAttribute(global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.appGroupsPropertyName, IsRequired=true, IsKey=false, IsDefaultCollection=false)]
public global::Business.Utility.SendToTest.Configuration.AppGroupSettingsCollection appGroups
{
get
{
return ((global::Business.Utility.SendToTest.Configuration.AppGroupSettingsCollection)(base[global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.appGroupsPropertyName]));
}
set
{
base[global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.appGroupsPropertyName] = value;
}
}
#endregion
}
}
namespace Business.Utility.SendToTest.Configuration
{
...
And here is my config file:
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="SendToTestConfig" type="System.Configuration.ConfigurationSectionGroup, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<section name="sendToTestIndexConfig" type="Business.Utility.SendToTest.Configuration.SendToTestIndexConfig, Business.Utility.SendToTest.Configuration"/>
</sectionGroup>
etc.
</configSections>
<SendToTestConfig xmlns="Business.Utility.SendToTest.Configuration">
<sendToTestIndexConfig>
<appGroups>
<!-- TODO Change indexFileNamePrefix to desired value-->
<appGroupSettings name="ItemBased" indexFileNamePrefix="S" indexFolderLocation="C:\TFS\Mainline\Business.Utility.SendToTest\Business.Utility.SendToTest" imageFolderLocation="C:\TFS\Mainline\Business.Utility.SendToTest\Business.Utility.SendToTest">
<topicEntries>
<topicEntry topicIDSuffix="DATE" addDuplicateVSAMEntry="true">
<itemSubFields>
<subField index="0" typeName="Date"/>
</itemSubFields>
</topicEntry>
<topicEntry topicIDSuffix="Item" addDuplicateVSAMEntry="true">
<itemSubFields>
<subField index="0" typeName="ItemNumber"/>
</itemSubFields>
</topicEntry>
<topicEntry topicIDSuffix="DESCR">
<itemSubFields>
<subField index="0" typeName="LongDescription"/>
</itemSubFields>
</topicEntry>
</topicEntries>
</appGroupSettings>
</appGroups>
</sendToTestIndexConfig>
</SendToTestConfig>
...
</configuration>
I have tried all of the following, but I keep getting null for the first few config (which I think makes sense), and getting the abovementioned exception for the rest.
//These return null:
SendToTestIndexConfig config = SendToTestIndexConfig.Instance;
//SendToTestIndexConfig config = (SendToTestIndexConfig) ConfigurationManager.GetSection("sendToTestIndexConfig");
//SendToTestIndexConfig configb = (SendToTestIndexConfig)WebConfigurationManager.GetSection("sendToTestIndexConfig");
//SendToTestIndexConfig configc = (SendToTestIndexConfig)WebConfigurationManager.OpenWebConfiguration(null).GetSection("sendToTestIndexConfig");
//SendToTestIndexConfig configd = (SendToTestIndexConfig)WebConfigurationManager.GetWebApplicationSection("sendToTestIndexConfig");
//SendToTestIndexConfig configf = (SendToTestIndexConfig)WebConfigurationManager.GetSection("sendToTestIndexConfig");
////These throw a "parameterless constructor error" on object "SendToTestConfig/sendToTestIndexConfig"
//SendToTestIndexConfig configg = (SendToTestIndexConfig)WebConfigurationManager.GetSection("SendToTestConfig/sendToTestIndexConfig");
//SendToTestIndexConfig configh = (SendToTestIndexConfig)WebConfigurationManager.OpenWebConfiguration(null).GetSection("SendToTestConfig/sendToTestIndexConfig");
//SendToTestIndexConfig configi = (SendToTestIndexConfig)WebConfigurationManager.GetWebApplicationSection("SendToTestConfig/sendToTestIndexConfig");
//SendToTestIndexConfig configj = (SendToTestIndexConfig)WebConfigurationManager.GetSection("SendToTestConfig/sendToTestIndexConfig");
I'm guessing it has something to do with my naming. ConfigurationManager.AppSettings works fine, so I know I have the right web.config.
Actually, the solution to the problem is succinctly stated in an example for the GenericEnumConverter on MSDN. I did the same thing you probably did and explicitly set the [TypeConverter(typeof(GenericEnumConverter))] attrbibute on one of my configuration section properties and got the same error. According to the documentation for GenericEnumConverter linked to above, you do not need to set this attribute in order to use the GenericEnumConverter type converter--it is called implicitly by the framework. Remove that attribute from your configuration property specifications, and this error should disappear and everything should just work.
Here is an example of a configuration section property that uses an Enum:
public enum UsernameFormat
{
DownLevelDomainName,
UsernameOnly,
UserPrincipalName
}
public class WindowsADElement : ConfigurationElement
{
// This property will implicitly use the GenericEnumConverter type converter.
[ConfigurationProperty("usernameFormat", IsRequired=true, DefaultValue=UsernameFormat.UserPrincipalName)]
public UsernameFormat UsernameFormat
{
get { return (UsernameFormat)this["usernameFormat"]; }
set { this["usernameFormat"] = value; }
}
Then, to use it in code:
MyConfigurationSection config = ConfigurationManager.GetSection("myConfigurationSection") as MyConfigurationSection;
UsernameFormat format = config.UsernameLookup.WindowsAD.UsernameFormat;
Hope that helps.
I am not sure if this is the best solution, but I was able to get around the problem. The error was because I was using the GenericEnumTypeConverter class to convert the config strings to the AppGroup and SubFieldTypes enums. I created my own custom TypeConverters, and it solved the problem. Apparently, GenericEnumTypeConverter has no parameterless constructors, requiring the enum type for the constructor. Would love to know if there is a way to use GenericEnumTypeConverter, but this worked for me.
This clued me in on the answer: Configuration Error With Custom Behaviour
Our automatic maintenance procedure sends out provisioning updates like this to our devices overnight:
<wap-provisioningdoc>
<characteristic type="SoftwareDisable">
<characteristic type="DisabledSystemFiles">
<parm name="Labyrinth.exe" />
</characteristic>
</characteristic>
</wap-provisioningdoc>
This works fine, apart from it pops up a box asking the user if they want to restart now or later, specifically:
Restart
Recent changes to your device require a
restart. During this process you cannot make
or receive phone calls, including emergency
calls. Restart your device now?
Now Later
This is of course difficult to do because there is no user, just racks and racks of devices sitting there by themselves.
So, is there any way of not popping this message up and just restarting the device automatically? Possibly some registry setting or something?
You can soft reset your device via code.
Just need to p/invoke
public enum SystemPowerStates : uint
{
/// <summary>
/// On state.
/// </summary>
On = 0x00010000,
/// <summary>
/// No power, full off.
/// </summary>
Off = 0x00020000,
/// <summary>
/// Critical off.
/// </summary>
Critical = 0x00040000,
/// <summary>
/// Boot state.
/// </summary>
Boot = 0x00080000,
/// <summary>
/// Idle state.
/// </summary>
Idle = 0x00100000,
/// <summary>
/// Suspend state.
/// </summary>
Suspend = 0x00200000,
/// <summary>
/// Reset state.
/// </summary>
Reset = 0x00800000
}
[DllImport("coredll.dll")]
internal static extern int SetSystemPowerState(string psState, int StateFlags, int Options);
/// <summary>
/// Defines the System power requirement flags
/// </summary>
public enum PowerReqFlags : uint
{
POWER_NAME = 0x00000001,
POWER_FORCE = 0x00001000,
}
And call the function SetSystemPowerState, I use it enclosed in another method.
private static void DeviceReset()
{
SetSystemPowerState(
null,
(int)SystemPowerStates.Reset,
(int)PowerReqFlags.POWER_FORCE);
}
Here's an example:
public void DoSomething(String param1, String param2)
{
if (param1 == null) throw new ArgumentNullException("param1");
if (param2 == null) throw new ArgumentNullException("param2");
}
2 different reasons for an ArgumentNullException. MSDNs String.Format Example shows 2 different reasons for the FormatException. So, is it done this way:
/// <exception cref="ArgumentNullException">
/// <paramref name="param1"/> is null.
/// </exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="param2"/> is null.
/// </exception>
or some other way?
/// <exception cref="ArgumentNullException">
/// Some other way to show the 2 reasons with an "-or-" between them.
/// </exception>
If you think each of the rows of the docs as being one <exception cref=...> </exception>, then logically the correct way to do it is using your second alternative:
/// <exception cref="ArgumentNullException">
/// <p><paramref name="param1"/> is null. </p>
/// <p>- or - </p>
/// <p><paramref name="param2"/> is null. </p>
/// </exception>
You can use 'p' elements to denote the lines.