I am trying to run a block of code when my WPF window is not open. Following code is not working please check it and let me know where I am doing wrong.
var window = IsWindowOpen<Window>(this.Title);
if(window)
{
//do something
}
else
{
// My code block
}
public static bool IsWindowOpen<T>(string name = null) where T : Window
{
var windows = Application.Current.Windows.OfType<T>();
return string.IsNullOrEmpty(name) ?
Application.Current.Windows.OfType<T>().Any() :
Application.Current.Windows.OfType<T>().Any(w => w.Name.Equals(name));
}
You are passing Window.Title, but then in your IsWindowOpen function you are trying to match Window.Name
Related
First ever post in here, if I made any mistake in the post pls tell me so I can fix it
So I'm trying to make classes to handle most of the code, one of the things I wanted to do was to have one handle all the opening and making of form instances. Doing a .Show(); on instances that already exist was simple as I know the order they are created I can just Form _form = Application.OpenForms[i]; to grab the instance, but when it's not already created I couldn't find a way to deal with it, I read a bit unto it but couldn't find something that really fit what I wanted to do, something something about reflection seemed to be the right path but couldn't get it to work, so some light in the matter would be very appreciated.
In a nutshell I'm trying to make something like:
(I know something similar is not possible but I think this is the easiest way to explain exactly what I seek. A workaround I made was to have the code to generate each of the Forms into a switch and just send their number, so it's what I'm gonna use if I can't find a better solution, but I wanted to learn a "proper/cleaner" way of achieving this)
static public bool MakeForm(string name)
{
name _name = new name();
_name.Show();
}
[Edit: I realized that this is irrelevant for my project cuz I can just ready up every single Form on login, but I still hope to know how to do this if any of yall can show me how to/where to read]
I am not sure If I understand you correctly, but I think you need a factory class.
public static class Factory
{
public static Form Create(string name)
{
switch (name)
{
case "FormA":
return new FormA();
case "FormB":
return new FormB();
}
}
}
Than you can create your forms by name.
Factory.Create("FormA").Show();
Here's a simple example using the Reflection approach:
private void button1_Click(object sender, EventArgs e)
{
Form f2 = TryGetFormByName("Form2");
if (f2 != null)
{
f2.Show();
}
}
public Form TryGetFormByName(string formName)
{
var formType = System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(T => (T.BaseType == typeof(Form)) && (T.Name == formName))
.FirstOrDefault();
return formType == null ? null : (Form)Activator.CreateInstance(formType);
}
Here's an alternate version that checks to see if the form is already open:
public Form TryGetFormByName(string formName)
{
// See if it's already open:
foreach (Form frm in Application.OpenForms)
{
if (frm.Name == formName)
{
return frm;
}
}
// It's not, so attempt to create one:
var formType = System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(T => (T.BaseType == typeof(Form)) && (T.Name == formName))
.FirstOrDefault();
return formType == null ? null : (Form)Activator.CreateInstance(formType);
}
Creating an app that on tap of an webview input field, has to do an action. Catching and starting the selected action works fine, but due to it being started by clicking an input field, the keyboard is requested. On Android < Version 9, my currently code works just fine to hide the keyboard, but on Android Version 9, it doesn't.
I have tried all manor or combination of what was deemed the top answer on this post, but none have worked for my app on Android 9
Below is a bit of my code from my MainActivity, where the instance of my keyboard service implementation is created. the MainActivity code is then followed by my Keyboard service implementation made for android.
[Activity(Label = "Dental.App", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, WindowSoftInputMode = SoftInput.StateAlwaysHidden) ]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
...
DependencyService.Get<IServiceCollection>().SetKeyboardService(new KeyboardService(this, GetInputMethodManager()));
...
}
public InputMethodManager GetInputMethodManager()
{
return (InputMethodManager)GetSystemService(Context.InputMethodService);
}
}
public class KeyboardService : IKeyboardService
{
private InputMethodManager inputMethodManager;
private readonly object mainActivity;
public KeyboardService(object activity, InputMethodManager methodManager)
{
mainActivity = activity;
inputMethodManager = methodManager;
}
public bool IsKeyboardShown => inputMethodManager.IsAcceptingText;
public void HideKeyboard()
{
if (inputMethodManager == null || !(mainActivity is Activity activity)) return;
Logging.Log(LogType.Information, $"Attempting to Hide Keyboard via 1st method...");
//var view = activity.CurrentFocus;
var view = activity.FindViewById(Android.Resource.Id.Content).RootView;
if (view == null) Logging.Log(LogType.Warning, $"Failed to get View from Activity...");
var token = view?.WindowToken;
if (token == null) Logging.Log(LogType.Warning, $"Failed to get Token from View...");
var success = inputMethodManager.HideSoftInputFromWindow(token, HideSoftInputFlags.None);
Logging.Log(LogType.Information,
$"{nameof(inputMethodManager.HideSoftInputFromWindow)} returned => {success}");
if(success) view?.ClearFocus();
if (!IsKeyboardShown)
{
view?.ClearFocus();
return;
}
Logging.Log(LogType.Warning,
$"Failed to Hide Keyboard via {nameof(inputMethodManager.HideSoftInputFromWindow)}...");
HideKeyboardAttemptTwo(activity);
}
private void HideKeyboardAttemptTwo(Activity activity)
{
Logging.Log(LogType.Information, $"Attempting to Hide Keyboard via 2nd method...");
//var view = activity.CurrentFocus;
var view = activity.FindViewById(Android.Resource.Id.Content).RootView;
if (view == null) Logging.Log(LogType.Warning, $"Failed to get View from Activity...");
var token = view?.WindowToken;
if (token == null) Logging.Log(LogType.Warning, $"Failed to get Token from View...");
inputMethodManager.ToggleSoftInputFromWindow(token, ShowSoftInputFlags.None, HideSoftInputFlags.None);
if (!IsKeyboardShown)
{
view?.ClearFocus();
return;
}
Logging.Log(LogType.Warning, $"Failed to Hide Keyboard via {nameof(inputMethodManager.ToggleSoftInputFromWindow)}...");
}
public void ReInitializeInputMethod()
{
inputMethodManager = InputMethodManager.FromContext((Context) mainActivity);
}
None of the null check are coming back true, i.e nothing is null. The variable called success in the method HideKeyboard is returning false in 99% of all cases where it is called on a android version 9. In the 1% of the cases where it is true, the keyboard is still open. If the keyboard is still shown at the end of HideKeyboard, then the code attempts to close the keyboard via toggling it in the method HideKeyboardAttemptTwo. Doing it either of theses ways on Android 9 does not work, however running the exact same code on an Android 7.1 works just fine.
I'm not entirely sure that i have implemented the use of ToggleSoftInputFromWindow correctly, it is intended to only be able to run when the keyboard is open, i.e always used to hide the keyboard.
To reiterate my question: How do it successfully hide the keyboard on an Android 9.
If any additional information is needed, just ask, and i will attempt to find and supply it.
I uses this for my app, give it a try
Interface in main project
namespace *.Services.Interfaces
{
public interface IForceKeyboardDismissalService
{
void DismissKeyboard();
}
}
Phone specific code
using Plugin.CurrentActivity; //Nugget used to get activity
[assembly: Xamarin.Forms.Dependency(typeof(AndroidForceKeyboardDismissalService))]
namespace *.Droid.PhoneSpecific
{
public class AndroidForceKeyboardDismissalService : IForceKeyboardDismissalService
{
public void DismissKeyboard()
{
var imm = InputMethodManager.FromContext(CrossCurrentActivity.Current.Activity.ApplicationContext);
imm?.HideSoftInputFromWindow(CrossCurrentActivity.Current.Activity.Window.DecorView.WindowToken, HideSoftInputFlags.NotAlways);
var currentFocus = CrossCurrentActivity.Current.Activity.CurrentFocus;
if (currentFocus != null && currentFocus is EditText)
currentFocus.ClearFocus();
}
}
}
Usage
DependencyService.Get<IForceKeyboardDismissalService>().DismissKeyboard();
Let me know if its working for you.
To fix my problem i injected some JavaScript into the Webview, wherein i unfocused the input field, that was clicked.
On my Webview class i created a method that, given the string id of an element, would toggle whether or not that element is focused. As a second input, a boolean can be supplied, but defaulted to True, to indicate whether or not, you only want to unfocus the element.
public class AdvancedWebView : HybridWebView
{
...
public void ToggleElementFocus(string elementId, bool onlyUnFocus = true)
{
var js = GetJsInvertFocus(elementId, onlyUnFocus);
InjectJavaScript(js);
// Logging.Logging.Log(LogType.Information, $"Injected Javascript => {js}");
}
...
private string GetJsInvertFocus(string elementId, bool onlyUnFocus)
{
var builder = new StringBuilder();
builder.Append($"if (document.getElementById('{elementId}'))");
builder.Append("{");
builder.Append($"var element = document.getElementById('{elementId}');");
builder.Append($"if (element === document.activeElement)");
builder.Append("{");
builder.Append($"element.blur();");
builder.Append("}");
builder.Append($"else if({onlyUnFocus} == False)");
builder.Append("{");
builder.Append($"element.focus();");
builder.Append("}");
builder.Append("}");
return builder.ToString();
}
...
}
I'm extending the HybridWebview from XLabs, as it already has the functionality to inject JavaScript into the Webview. So that is where i get the InjectJavaScript method from.
On my page in my app, with the Webview, i then have a method that runs, when the element is clicked. To get a click event when clicking the Webview look at this link. During the method i figure out what the element id is from the event arguments, and then use this id to inject the JavaScript shown above, to unfocus the element, causing the keyboard to not appear at all. Below my OnClicked method can be seen.
public partial class DentalWebPage : AdvancedTabbedPage
{
...
private void DentalWebView_OnClicked(object sender, ClickEvent e)
{
try
{
if (LogUserPosition(sender, e)) return;
SwapToScanningTap();
}
catch (Exception ex)
{
Logging.Log(LogType.Exception,
ex.GetType().Namespace == typeof(BaseException).Namespace
? $"{ex.GetType()} => {ex}"
: $"{ex.GetType()} => {ex.Message}; Stacktrace => {ex.StackTrace}");
}
}
private bool LogUserPosition(object sender, ClickEvent e)
{
if (Config.DebugMode) Logging.Log(LogType.Debug, $"WebView was clicked...");
if (Config.DebugMode) Logging.Log(LogType.Debug, $"Element that was clicked is the following one => {e.Element}");
var success = Enum.TryParse(e.Element.Split(' ')[1].Split('=')[1], out clickedInputId);
if (!success && !(clickedInputId == InputId.MainContent_TextBoxInputStr ||
clickedInputId == InputId.MainContent_TextBoxScanOrder ||
clickedInputId == InputId.MainContent_TextBoxSelectProd ||
clickedInputId == InputId.MainContent_TextBoxStockReturn))
return true;
if (Config.DebugMode && webPageEnding == WebsiteControllers.Stock.ToString().ToLowerInvariant())
Logging.Log(LogType.Debug, $"WebView was clicked while on the stock page...");
return false;
}
private void SwapToScanningTap()
{
PerformOnMainThread(() =>
{
CurrentPage = Children[1];
ScanningToggle.IsToggled = true;
try
{
var isKeyboardShown = services.KeyboardService.IsKeyboardShown;
if (Config.DebugMode) Logging.Log(LogType.Debug, $"IsKeyboardShown returns => {isKeyboardShown}");
DentalWebView.ToggleElementFocus(clickedInputId.ToString());
}
catch (ObjectDisposedException)
{
if (DisposedReattempt) throw;
if (Config.DebugMode)
Logging.Log(LogType.Debug,
$"Input Method has been Disposed; Attempting to reinitialize it and rerun the {nameof(SwapToScanningTap)} method ones again");
DisposedReattempt = true;
services.KeyboardService.ReInitializeInputMethod();
SwapToScanningTap();
}
});
}
...
private void PerformOnMainThread(Action action)
{
try
{
Device.BeginInvokeOnMainThread(action);
}
catch (Exception ex)
{
Logging.Log(LogType.Exception,
ex.GetType().Namespace == typeof(BaseException).Namespace
? $"{ex.GetType()} => {ex}"
: $"{ex.GetType()} => {ex.Message}; Stacktrace => {ex.StackTrace}");
}
}
}
If you wish to get a understanding of the format of the string contained in e.Element, then go and look at the link supplied earlier.
Fell free to ask further questions, in case i missed something.
I'm writing plugin/interface for CAM/CAD software and I use this code to open a "SaveWindow".
public void Run(string theMode)
{
try
{
if (theMode == "SaveWindow")
{
string aPictureString = GetPictureString();
StartInterface(null, theMode, CreateAndSaveTheToolList(aPictureString));
}
else
{
string aPipeId = GetRandomString();
itsServerStream = new NamedPipeServerStream(aPipeId, PipeDirection.In, 1);
ThreadPool.QueueUserWorkItem(this.ListenToStream);
StartInterface(aPipeId, theMode, "");
StartNamedPipe(aPipeId);
itsRefreshThread = new Thread(this.RefreshTools);
itsRefreshThread.Start();
if (!InitLogger(Path.GetDirectoryName(this.GetType().Assembly.Location)))
{
MessageBox.Show(//Secured code);
return;
}
}
itsLogger.Info("Run execute was successful.");
}
catch (Exception aException)
{
//Secured code
}
LogManager.ResetConfiguration();
}
If there is an interface open and I click the plugin button again it opens another multiple one. How do I code to not to open the second one if the first is open.
Your current situation is something like
if (condition)
{
// Open your window
}
else
{
// Do something else
}
So every time you satisfy your condition another instance of your window is opened.
You can get around this problem by checking whether your window is already open like this
bool isOpen = false;
if (!isOpen)
{
// The window isn't open so open it
isOpen = true;
}
else
{
// The window is already open so don't open it again
}
In this case the question is in the situations when your condition is satisfied but your window is open what would you like to do?
Simply adding the isOpen check to your open window path like this
if (condition && !isOpen)
{
// Open your window
isOpen = true;
}
else
{
// Do something else
}
Will mean that whenever your condition is satisfied and the window is already open your code will "Do something else".
An alternative approach is something like this
if (condition)
{
if (!isOpen)
{
// Open your window
isOpen = true;
}
else
{
// Do something else
}
}
else
{
// Do something else
}
This means that when your condition is satisfied you open the window if it isn't open and do something else if it is. Then your third case is the second "Do something else" path for when the condition isn't satisfied.
Firstly, a disclaimer, what you're about to witness is my first bit of coding for almost 20 years. I'm new to C# and WPF, trying to get my head WPF is more than a challenge.
For the past month I've been working on a pet project console application, which has been performing well. I'm now trying to take it another step further by adding a modern GUI to the project.
I'd like to emulate a console (just the basic outpu functionality) by using a WPF textblock wrapped inside a scroller in a WPF window. You can see the original console application in action here to get a better idea of the kind of console output I'm trying to emulate. But I'm having a major problem with basic function calls, and I assume it's because I don't fully understand how WPF/C# work under the hood.
The Application starts in code via Main() like so:
class Program
{
public static ConsoleWindow MainConsole = new ConsoleWindow();
[STAThread]
static void Main(string[] args)
{
Application MyApplication = new Application();
MyApplication.Run(MainConsole);
// The following code does not work, it produces no output in the Textblock
MainConsole.WriteLine("Crystal Console");
MainConsole.WriteLine("Version: " + Properties.Settings.Default.BuildVersion);
MainConsole.WriteLine("Current Time: " + DateTime.Now);
MainConsole.WriteLine("Last Login: " + Properties.Settings.Default.dateLastLogin);
}
}
The problem is that the methods called don't seem to have any affect on the content of the textblock.
Although I'm about to give a lot of information just in case it's needed, the question itself is quite simple: Why does the Textblock update fine when taking content from a textbox control on the same window, but doesn't show any updates when the same method is called in Main() ?
For testing purposes the window has a few Textboxes that call the .WriteLine method inside the window, and THAT works, so I know there isn't a problem with the .WriteLine code, which you can see here:
public void WriteLine(string Message = null, string Sender = null)
{
_Console.AddElement(new ConsoleElement(Sender, Message + "\n"));
_Console.DisplayContent(ConsoleTextBlock);
ConsoleScroller.ScrollToEnd();
}
Here is the code for the console itself in case it's needed, the class "ConsoleElement" is essentially just a object that contains the messages to be displayed in the Textblock as well as the formatting for each one.
class ConsoleStream
{
IList<ConsoleElement> ConsoleElements = new List<ConsoleElement>();
public void AddElement(ConsoleElement NewElement)
{
if (NewElement.Sender == null) // Sender is System not user.
{
NewElement.Content = " " + NewElement.Content;
NewElement.Font = new FontFamily("Arial");
NewElement.FontSize = 12;
}
ConsoleElements.Add(NewElement);
}
public void ClearElements()
{
ConsoleElements.Clear();
}
public void DisplayContent(TextBlock sender)
{
sender.Text = null;
foreach (ConsoleElement Message in ConsoleElements)
{
//If message is a status update, i.e. has no sender, format it as a system message.
if (Message.Sender != null)
{
sender.Inlines.Add(new Run(Message.Sender + ": ") { Foreground = Message.SenderColour, FontFamily = Message.Font, FontSize = Message.FontSize });
}
//if message has a sender it's either the user or the AI. Format it as a user message.
if (Message.Sender != null) sender.Inlines.Add(new Run(Message.Content) { Foreground = Message.ContentColour, FontFamily = Message.Font, FontSize = Message.FontSize });
else sender.Inlines.Add(new Run(Message.Content) { Foreground = Message.SystemColour, FontFamily = Message.Font, FontSize = Message.FontSize });
}
}
}
MyApplication.Run(MainConsole); takes control of the thread, the code after it doesn't execute until after you close the window.
Move the code to the load (or init) method of your ConsoleWindow
I found this code on SO to automatically dismiss a confirm dialog, but it is not working in Firefox.
The problem is, var windowButton = new WindowsEnumerator().GetChildWindows(window.Hwnd, w => w.ClassName == "Button"
&& new WinButton(w.Hwnd).Title == "OK").FirstOrDefault();
Always returns null. Is there another way to get the handle of the dialog button in firefox?
public class OKDialogHandler : BaseDialogHandler {
public override bool HandleDialog(Window window) {
var button = GetOKButton(window);
if (button != null) {
button.Click();
return true;
} else {
return false;
}
}
public override bool CanHandleDialog(Window window) {
return GetOKButton(window) != null;
}
private WinButton GetOKButton(Window window) {
var windowButton = new WindowsEnumerator().GetChildWindows(window.Hwnd, w => w.ClassName == "Button"
&& new WinButton(w.Hwnd).Title == "OK").FirstOrDefault();
if (windowButton == null)
return null;
else
return new WinButton(windowButton.Hwnd);
}
}
The controls on the Firefox alert() dialog are not enumerable. That is, they don't exist as separate windows like they do in IE. The best way to approach this is to create a new DialogHandler class that implements IDialogHandler. In the constructor, you can pass in the Firefox instance for which the dialog appears, and you can use the following codeto send JavaScript across to Firefox to manipulate the dialog:
FFDocument nativeDoc = firefox.NativeDocument as FFDocument;
// ClientPort has several WriteAndRead... functions,
// and takes a variable list of arguments for the script
// to be executed.
nativeDoc.ClientPort.WriteAndRead(script);
You can use the JavaScript below to click on the OK and Cancel buttons on an alert() or confirm() dialog.
private const string DialogIsConfirmScript = "typeof getWindows()[{0}].document.documentElement.getButton('accept') !== 'undefined' && typeof getWindows()[{0}].document.documentElement.getButton('cancel') !== 'undefined';";
private const string DialogIsAlertScript = "typeof getWindows()[{0}].document.documentElement.getButton('accept') !== 'undefined' && typeof getWindows()[{0}].document.documentElement.getButton('cancel') !== 'undefined' && getWindows()[{0}].document.documentElement.getButton('cancel').hidden;";
private const string ClickCancelButtonScript = "getWindows()[{0}].document.documentElement.getButton('cancel').click()";
private const string ClickOKButtonScript = "getWindows()[{0}].document.documentElement.getButton('accept').click()";
private const string WindowClassName = "MozillaDialogClass";
A more complete implementation, which wraps the native IE alert() and confirm() handling in a common interface and adds Firefox handling is available at http://pastebin.com/ZapXr9Yf