I was trying to create a custom window which changes its theme based on the windows 10 theme (Light or Dark). I was able to listen to the windows 10 theme change at runtime. but when i set my custom dependency property value (WindowTheme), the application throws exception :
Exception thrown: 'System.InvalidOperationException' in
WindowsBase.dll Exception thrown:
'System.Reflection.TargetInvocationException' in mscorlib.dll
Here is my Code:
Window.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using UWPHost.Enums;
using UWPHost.Utilities;
using static UWPHost.Utilities.NativeMethods;
namespace UWPHost
{
public class Window:System.Windows.Window
{
public Window()
{
}
private void SetTheme()
{
ResourceDictionary resourceDict = Application.LoadComponent(new Uri("UWPHost;component//Themes/res/Window.xaml", System.UriKind.RelativeOrAbsolute)) as ResourceDictionary;
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(resourceDict);
this.Style = (Style)Application.Current.Resources["GenericWindow"];
new ThemeUtility().Init(this);
}
public void SwitchTheme(Theme theme)
{
WindowTheme = theme;
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var hWndSource = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
hWndSource.CompositionTarget.BackgroundColor = (Color)ColorConverter.ConvertFromString(CompositionTargetColor.ToString());
var nonClientArea = new Margins
{
left = ResizeFrameWidth,
top = (int)CaptionHeight,
bottom = ResizeFrameWidth,
right = ResizeFrameWidth
};
DwmExtendFrameIntoClientArea(hWndSource.Handle, ref nonClientArea);
hWndSource.AddHook(new WndProc(this).GetWndProc);
SetWindowPos(hWndSource.Handle, new IntPtr(), 0, 0, 0, 0, 0x0020 | 0x0002 | 0x0001);
SetWindowLong(hWndSource.Handle, GWL_STYLE, GetWindowLong(hWndSource.Handle, GWL_STYLE) & ~WS_SYSMENU);
SetTheme();
}
#region Dependency Properties
public double CaptionHeight
{
get { return (double)GetValue(CaptionHeightProperty); }
set { SetValue(CaptionHeightProperty, value); }
}
public static readonly DependencyProperty CaptionHeightProperty = DependencyProperty.Register("CaptionHeight", typeof(double), typeof(Window), new PropertyMetadata(33.0));
public int ResizeFrameWidth
{
get { return (int)GetValue(ResizeFrameWidthProperty); }
set { SetValue(ResizeFrameWidthProperty, value); }
}
public static readonly DependencyProperty ResizeFrameWidthProperty = DependencyProperty.Register("ResizeFrameWidth", typeof(int), typeof(Window), new PropertyMetadata(10));
public String CompositionTargetColor
{
get { return (String)GetValue(CompositionTargetColorProperty); }
set { SetValue(CompositionTargetColorProperty, value); }
}
public static readonly DependencyProperty CompositionTargetColorProperty = DependencyProperty.Register("CompositionTargetColor", typeof(String), typeof(Window), new PropertyMetadata("#FFFFFF"));
public Theme WindowTheme
{
get { return (Theme)GetValue(WindowThemeProperty); }
set { SetValue(WindowThemeProperty, value); }
}
public static readonly DependencyProperty WindowThemeProperty = DependencyProperty.Register("WindowTheme", typeof(Theme), typeof(Window), new PropertyMetadata(Theme.Default));
#endregion
}
}
ThemeUtility.cs
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Management;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using UWPHost.Enums;
namespace UWPHost.Utilities
{
public class ThemeUtility
{
private const string ThemeRegistry= #"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
private const string RegistryValue= "AppsUseLightTheme";
private readonly string query;
private readonly WindowsIdentity CurrentUser;
ManagementEventWatcher ThemeWatcher;
Window window;
public ThemeUtility()
{
CurrentUser = WindowsIdentity.GetCurrent();
query= string.Format(CultureInfo.InvariantCulture,#"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath = '{0}\\{1}' AND ValueName = '{2}'", CurrentUser.User.Value,ThemeRegistry.Replace(#"\", #"\\"),RegistryValue);
}
public void Init(Window window)
{
this.window = window;
InitSystemThemeWatcher();
}
private void InitSystemThemeWatcher()
{
try
{
ThemeWatcher = new ManagementEventWatcher(query);
ThemeWatcher.EventArrived += (sender, args) =>
{
//This code is executed when windows 10 theme changes
if(GetSystemTheme()==Theme.Dark)
{
//Here i'm setting the property of window
window.SwitchTheme(Theme.Dark);
}
else
{
window.SwitchTheme(Theme.Light);
}
};
ThemeWatcher.Start();
}
catch(Exception)
{
throw new Exception("Error Unable to add theme listener.");
}
Theme initialTheme = GetSystemTheme();
}
private static Theme GetSystemTheme()
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(ThemeRegistry))
{
object registryValueObject = key?.GetValue(RegistryValue);
if (registryValueObject == null)
{
return Theme.Light;
}
int registryValue = (int)registryValueObject;
return registryValue > 0 ? Theme.Light : Theme.Dark;
}
}
}
}
Exception thrown: 'System.InvalidOperationException' in WindowsBase.dll
From this article i was able to know it can be done using INotifyPropertyChanged, but i dont know how to implement it in my case.
Note:The WindowTheme property is of type enum and have values Light,Dark,Default
The ManagementEventWatcher.EventArrived handler is apparently not called in the UI thread of your application.
A dependency property can only be accessed in the thread in which the owning object was created.
Use the Window's Dispatcher to marshal the execution to the thread where the Window was created, i.e. the UI thread:
public void SwitchTheme(Theme theme)
{
Dispatcher.Invoke(() => WindowTheme = theme);
}
Related
I have created a UserControl which has 2 properties.
See my code here:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel.Design;
namespace Controls
{
public partial class UserControl1: Panel
{
bool reset = false;
public bool _reset
{
get { return reset; }
set
{
reset = value;
if (value == true)
{
this.Controls.Clear();
cntCount = 0;
reset = false;
}
}
}
int cntCount = 0;
public int _cntCount
{
get { return cntCount; }
set
{
cntCount = value;
populate();
}
}
public UserControl1()
{
InitializeComponent();
}
void populate()
{
this.Controls.Clear();
IDesignerHost mDes = null;
mDes = (IDesignerHost)GetService(typeof(IDesignerHost));
for (int t = 0; t < cntCount; t++)
{
Button b = mDes.CreateComponent(typeof(Button), GenerateNewPaneName()) as Button;
this.Controls.Add(b);
}
}
protected string GenerateNewPaneName()
{
int curNum = 1;
bool bDuplicateName;
IReferenceService refsvc = GetService(typeof(IReferenceService)) as IReferenceService;
string curTry;
// Get a new component name
do
{
curTry = "b_" + curNum;
bDuplicateName = (refsvc.GetReference(curTry) != null);
curNum++;
}
while (bDuplicateName);
return curTry;
}
}
}
Dragging that control on the form is no problem.
If I enter a number into the property _cntCount, the control adds the required number of buttons inside my UserControl; no problem.
If I save the Form, close and reopen it, I get an error:
Duplicate component name 'b_1'. Component names must be unique and case-insensitive.
What do I miss?
Thanks in advance,
Murat
So initially while I was creating my Custom Control I was facing an issue where the handlers were not calling the CreatePlatform method, I was able to solve that with some help from you guys more information: MAUI CreatePlatformView is never called?
But ever since I have done that it seems that a lot of properties of my Editor have stopped working which is really weird since they should by default get wired from the existing MAUI control.
Here is the relevant code:
Shared code handler:
using System;
using Microsoft.Maui.Handlers;
#if ANDROID
using NativeView = AndroidX.AppCompat.Widget.AppCompatEditText;
#endif
#if IOS
using NativeView = UIKit.UITextView;
#endif
public partial class FreakyEditorHandler : ViewHandler<IFreakyEditor, NativeView>
{
#region ctor
public static CommandMapper<IFreakyEditor, FreakyEditorHandler> CommandMapper = new(ViewCommandMapper);
public FreakyEditorHandler() : base(FreakyEditorMapper)
{
}
public FreakyEditorHandler(IPropertyMapper pmapper, CommandMapper cmapper = null)
: base(pmapper ?? FreakyEditorMapper, cmapper ?? CommandMapper)
{
}
#endregion
#region Mappers
public static IPropertyMapper<IFreakyEditor, FreakyEditorHandler> FreakyEditorMapper = new PropertyMapper<IFreakyEditor, FreakyEditorHandler>(ViewMapper)
{
[nameof(IFreakyEditor.HasUnderline)] = MapHasUnderlineWithColor,
[nameof(IFreakyEditor.UnderlineColor)] = MapHasUnderlineWithColor
};
public static void MapHasUnderlineWithColor(FreakyEditorHandler handler, IFreakyEditor entry)
{
handler.HandleNativeHasUnderline(entry.HasUnderline, entry.UnderlineColor);
}
#endregion
protected override void ConnectHandler(NativeView platformView)
{
base.ConnectHandler(platformView);
}
protected override void DisconnectHandler(NativeView platformView)
{
base.DisconnectHandler(platformView);
}
}
My Native Handlers are as follows:
Android:
using System;
using Android.Content.Res;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using AndroidX.AppCompat.Widget;
using AndroidX.Core.Content;
using AndroidX.Core.Content.Resources;
using AndroidX.Core.Graphics;
using AndroidX.Core.View;
using Microsoft.Maui.Handlers;
using Color = Microsoft.Maui.Graphics.Color;
namespace MAUI.FreakyControls
{
public partial class FreakyEditorHandler
{
protected override AppCompatEditText CreatePlatformView()
{
var _nativeView = new AppCompatEditText(Context)
{
ImeOptions = ImeAction.Done,
Gravity = GravityFlags.Top,
TextAlignment = Android.Views.TextAlignment.ViewStart,
};
_nativeView.SetSingleLine(false);
_nativeView.SetHorizontallyScrolling(false);
return _nativeView;
}
private void HandleNativeHasUnderline(bool hasUnderline, Color underlineColor)
{
ColorStateList colorStateList;
var AndroidColor = underlineColor.ToNativeColor();
colorStateList = ColorStateList.ValueOf(hasUnderline ? AndroidColor : Android.Graphics.Color.Transparent);
ViewCompat.SetBackgroundTintList(PlatformView, colorStateList);
//colorFilter = hasUnderline ? ContextCompat.GetColor(this, AndroidColor, PorterDuff.Mode.SrcAtop);
//BlendModeColorFilterCompat.CreateBlendModeColorFilterCompat(
// AndroidColor, BlendModeCompat.SrcAtop) :
//BlendModeColorFilterCompat.CreateBlendModeColorFilterCompat(
// Android.Graphics.Color.Transparent, BlendModeCompat.SrcAtop);
//PlatformView.Background?.SetColorFilter(colorFilter);
}
}
}
iOS :
using System;
using CoreGraphics;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using UIKit;
using CoreAnimation;
using static MAUI.FreakyControls.Platforms.iOS.NativeExtensions;
namespace MAUI.FreakyControls
{
public partial class FreakyEditorHandler
{
CALayer bottomline;
protected override UITextView CreatePlatformView()
{
var _nativeView = new UITextView();
return _nativeView;
}
private void HandleNativeHasUnderline(bool hasUnderline, Color color)
{
if (hasUnderline)
{
var uiColor = color.ToNativeColor();
bottomline = BottomLineDrawer(uiColor);
bottomline.Frame = new CGRect(x: 0, y: PlatformView.Frame.Size.Height - 5,
width: PlatformView.Frame.Size.Width, height: 1);
PlatformView.Layer.AddSublayer(bottomline);
PlatformView.Layer.MasksToBounds = true;
}
else
{
bottomline?.RemoveFromSuperLayer();
}
}
}
}
Handler registration :
handlers.AddHandler(typeof(FreakyEditor), typeof(FreakyEditorHandler));
You can access my code base here: https://github.com/FreakyAli/MAUI.FreakyControls/tree/r1-gh/feat/freakyeditor
I have the following form with a method called setIndex
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ProjectName
{
public partial class SettingsWindow : Form
{
internal readonly static SettingsWindow Instance = new SettingsWindow { Visible = false };
public SettingsWindow()
{
InitializeComponent();
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
string defaultsearch = listBox1.GetItemText(listBox1.SelectedItem);
Core.RegistryHelper.SaveSetting("Config", "ds", defaultsearch);
if (defaultsearch == "aaa") {
Core.LandingUrlOrig = Core.DomainName + "/defaulturl1.php";
} else {
Core.LandingUrlOrig = Core.DomainName + "/defaulturl2.php";
}
}
public static void setIndex(int i)
{
listBox1.SelectedIndex = i;
}
On another form called MainWindow during its initialization I call:
public MainWindow()
{
SettingsWindow.setIndex(0);
}
The error I get is:
An object reference is required for the non-static field, method, or property 'SettingsWindow.listBox1'
Initially the listbox method wasn't static and thus invisible from MainWindow. But now, listbox appears to not exist, even if the form has been instantiated. How do I solve this? I'm just learning C#.
Thank you in advance
because the function setIndex is static you need to use the Instance property:
public static void setIndex(int i)
{
Instance.listBox1.SelectedIndex = i;
}
or don't make that function static and then use instance in the mainwindow function:
public void setIndex(int i)
{
listBox1.SelectedIndex = i;
}
public MainWindow()
{
SettingsWindow.Instance.setIndex(0);
}
I got two Classes One is derived from TreeView and the other from TreeNode:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Resources;
using ExtendedTreeView.Properties;
namespace ExtendedTreeView
{
public partial class ExtendedTreeView : System.Windows.Forms.TreeView
{
public ExtendedTreeView()
{
InitializeComponent();
ImageList il = new ImageList();
il.Images.Add("blank", Resources.favicon);
il.Images.Add("application", Resources.application);
il.Images.Add("computer", Resources.computer);
il.Images.Add("network", Resources.network);
il.Images.Add("session", Resources.session);
il.Images.Add("user", Resources.user);
il.Images.Add("usergroup", Resources.usergroup);
this.ImageList = il;
}
}
}
and:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.NetworkInformation;
using System.Management;
using System.Windows.Forms;
using Cassia;
using System.Security.Principal;
namespace GUI
{
public class Computer : System.Windows.Forms.TreeNode, IDictionaryEnumerator
{
private DictionaryEntry nodeEntry;
private IEnumerator enumerator;
public String computerName { get; private set; }
public Computer(String computername)
{
this.computerName = computername;
enumerator = base.Nodes.GetEnumerator();
this.Text = this.ipAdressen[0].ToString() + " - " + this.computerName;
this.ImageIndex = 2;
}
#region TreeNodeStuff
public string NodeKey
{
get
{
return nodeEntry.Key.ToString();
}
set
{
nodeEntry.Key = value;
}
}
public object NodeValue
{
get
{
return nodeEntry.Value;
}
set
{
nodeEntry.Value = value;
}
}
public DictionaryEntry Entry
{
get
{
return nodeEntry;
}
}
public bool MoveNext()
{
bool Success;
Success = enumerator.MoveNext();
return Success;
}
public object Current
{
get
{
return enumerator.Current;
}
}
public object Key
{
get
{
return nodeEntry.Key;
}
}
public object Value
{
get
{
return nodeEntry.Value;
}
}
public void Reset()
{
enumerator.Reset();
}
#endregion
}
}
But the TreeNode-Icons are stuck at Index 0. So my problem is, that im not able to set the ImageIndex-Property of the Computer TreeNode.
What am I doing wrong?
My question is all about URL Protocols.
I have registered a URL Protocol called mcm, but I noticed that everytime I run it from any web browser, t creates a new instance of the application. Is there any way to handle the protocol request in an already running instance?
For example, when uTorrent is using the torrent protocol It handles the request immediately without running the app again. I couldn't really find anything interesting about it, so I am asking here...
Here is the code I use to register the protocol:
private static void RegisterUrlProtocol()
{
UnregisterUrlProtocol();
RegistryKey rKey = Registry.ClassesRoot.OpenSubKey(UrlProtocol, true);
if (rKey == null)
{
rKey = Registry.ClassesRoot.CreateSubKey(UrlProtocol);
rKey.SetValue("", "URL: MazCraft Protocol");
rKey.SetValue("URL Protocol", "");
rKey = rKey.CreateSubKey(#"shell\open\command");
rKey.SetValue("", "\"" + Application.ExecutablePath + "\" %1");
}
if (rKey != null)
{
rKey.Close();
}
}
And the code to read the arguments:
private static bool CheckForProtocolMessage()
{
string[] arguments = Environment.GetCommandLineArgs();
if (arguments.Length > 1)
{
string[] args = arguments[1].Split(':');
args[1] = args[1].Replace("//", "");
if (args[0].Trim().ToUpper() == "MCM" && args.Length > 1)
{
string[] actionDetail = args[1].Split('=');
if (actionDetail[0].Trim().ToUpper() == "INSTALL" && actionDetail.Length > 1)
{
string id = actionDetail[1].Trim().Replace("/", "");
Funcs.ID = id;
return true;
}
}
}
return false;
}
Any help would be greatly appreciated :)
Greetings.
You could use a Mutex to detect an instance of the application that is already running and send the data over to the existing instance via Named Pipes.
Hope the following example helps.
you can swap out the named pipes object (in this case string) for whatever serializable object you like.
NamedPipe.cs
namespace SingleInstanceNP
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using System.IO;
public class NamedPipe<T> : IDisposable
{
#region Attribute and Properties
private string _pipeName;
private NamedPipeServerStream _pipeServer;
private bool _disposed;
private Thread _thread;
private bool _started;
#endregion
#region Constructors
public NamedPipe(NameTypes pipeType)
{
_disposed = false;
_started = false;
_pipeName = pipeType.ToString();
_thread = new Thread(Main);
_thread.SetApartmentState(ApartmentState.STA);
_thread.Name = "NamePipe: " + pipeType.ToString() + " Thread";
_thread.IsBackground = true;
}
~NamedPipe()
{
Dispose();
}
#endregion
#region Events
public delegate void Request(T t);
public event Request OnRequest;
#endregion
#region Public Methods
public static void Send(NameTypes pipeType, T t)
{
using (var npc = new NamedPipeClientStream(".", pipeType.ToString(), PipeDirection.Out))
{
var bf = new BinaryFormatter();
npc.Connect();
bf.Serialize(npc, t);
}
}
public static T Recieve(NameTypes pipeType)
{
using (var nps = new NamedPipeServerStream(pipeType.ToString(), PipeDirection.In))
{
return Recieve(nps);
}
}
public void Start()
{
if (!_disposed && !_started)
{
_started = true;
_thread.Start();
}
}
public void Stop()
{
_started = false;
if (_pipeServer != null)
{
_pipeServer.Close();
// disposing will occur on thread
}
}
public void Dispose()
{
_disposed = true;
Stop();
if (OnRequest != null)
OnRequest = null;
}
#endregion
private void Main()
{
while (_started && !_disposed)
{
try
{
using (_pipeServer = new NamedPipeServerStream(_pipeName))
{
T t = Recieve(_pipeServer);
if (OnRequest != null && _started)
OnRequest(t);
}
}
catch (ThreadAbortException)
{ }
catch (System.IO.IOException iox)
{
Console.WriteLine("ERROR: {0}", iox.Message);
Thread.Sleep(TimeSpan.FromSeconds(30));
}
catch (Exception ex)
{
Console.WriteLine("ERROR: {0}", ex.Message);
return;
}
}
}
private static T Recieve(NamedPipeServerStream nps)
{
var bf = new BinaryFormatter();
try
{
nps.WaitForConnection();
var obj = bf.Deserialize(nps);
if (obj is T)
return (T)obj;
}
// Catch the IOException that is raised if the pipe is
// broken or disconnected.
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
return default(T);
}
#region Enums
public enum NameTypes
{
PipeType1
}
#endregion
}
}
Program.cs
Please give credit for the APP GUID to What is a good pattern for using a Global Mutex in C#?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;
namespace SingleInstanceNP
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// get application GUID as defined in AssemblyInfo.cs
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
// unique id for global mutex - Global prefix means it is global to the machine
string mutexId = string.Format("Global\\{{{0}}}", appGuid);
using (var mutex = new Mutex(false, mutexId))
{
try
{
if (!mutex.WaitOne(0, false))
{
//signal existing app via named pipes
NamedPipe<string>.Send(NamedPipe<string>.NameTypes.PipeType1, "test");
Environment.Exit(0);
}
else
{
// handle protocol with this instance
Application.Run(new Form1());
}
}
finally
{
mutex.ReleaseMutex();
}
}
}
}
}
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace SingleInstanceNP
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// start listening for named pipe connections
var namedPipeString = new NamedPipe<string>(NamedPipe<string>.NameTypes.PipeType1);
namedPipeString.OnRequest += new NamedPipe<string>.Request(namedPipeString_OnRequest);
namedPipeString.Start();
}
void namedPipeString_OnRequest(string t)
{
MessageBox.Show(t);
}
}
}