I am trying to get a label widget to update when a key is pressed. For some reason cannot get the label text to update, even though the Gtk thread correctly registers the key press (eg, writes the key to console). This is mono 4.4.0 and gtk-sharp 2.12 on macos.
public MainWindow() : base(Gtk.WindowType.Toplevel)
{
Build();
this.KeyPressEvent += ToddlerKeyPressEventHandler;
}
public void ToddlerKeyPressEventHandler (object o, Gtk.KeyPressEventArgs args) {
Gtk.Application.Invoke(delegate {
string letter = args.Event.Key.ToString();
this.letterLabel.Text = letter;
Console.WriteLine(letter);
this.letterLabel.QueueDraw();
});
}
I think you're missing an attribute. Try this:
[GLib.ConnectBeforeAttribute]
public void ToddlerKeyPressEventHandler (object o, Gtk.KeyPressEventArgs args)
{
letterLabel.Text = args.Event.Key.ToString ();
}
Bear in mind that if the event handler is hooked up to the label widget this won't work unless you also ensure that the "Selectable" property for that label widget is set to true (checked).
Related
I'm using MRTK 2.3.0 trying to catch a "Grip Press" event from the Mixed Reality motion controller.
I've setup the Input Action in the MRTK Toolkit in the Hierarchy of Unity. I've also assigned the action to the controller's grip button in the Controller Definitions. I'm using the following code and made sure the Grip variable is assigned to the Grip Press event. Nothing happens... I'm able to catch touchpad and joystick, menu button press, but not Grip? Why?
According to this documentation: https://microsoft.github.io/MixedRealityToolkit-Unity/Documentation/MixedRealityConfigurationGuide.html the Grip should be a "float" as it's designed as single axis (I wonder why, since it's a button and not a trigger...). However, I'm trying to catch the event where I can... not working...
Anyone understand what the heck I'm trying to say here? (sorry, the code below also includes other events that I can catch without a hickup).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit;
public class TestInput : MonoBehaviour, IMixedRealityBaseInputHandler,
IMixedRealityInputHandler<Vector2>, IMixedRealityInputHandler
{
public MixedRealityInputAction Trigger, Grip, Scroll, Swipe, Joystick, DPad, TouchPadClicked, TouchPadTouched, MenuPressed;
public MixedRealityInputAction DPadUp, DPadDown, DPadLeft, DPadRight, JoystickUp, JoystickDown, JoystickLeft, JoystickRight;
private bool TouchpadPressed, MenuButtonPressed, GrabPressed, TouchDialogPadPressed, TouchpadClicked, Thouchpadtouched,
ThumbstickActive, ThumbstickPressed, VRManipulationStarted, ScrubStarted, PlayPause = false;
private void OnEnable()
{
IMixedRealityInputSystem inputSystem;
if (MixedRealityServiceRegistry.TryGetService<IMixedRealityInputSystem>(out inputSystem))
{
inputSystem?.RegisterHandler<IMixedRealityBaseInputHandler>(this);
inputSystem?.RegisterHandler<IMixedRealityInputHandler<Vector2>>(this);
inputSystem?.RegisterHandler<IMixedRealityInputHandler>(this);
inputSystem?.RegisterHandler<IMixedRealityInputHandler<float>>(this);
}
}
private void OnDisable()
{
IMixedRealityInputSystem inputSystem;
if (MixedRealityServiceRegistry.TryGetService<IMixedRealityInputSystem>(out inputSystem))
{
inputSystem?.UnregisterHandler<IMixedRealityBaseInputHandler>(this);
inputSystem?.UnregisterHandler<IMixedRealityInputHandler<Vector2>>(this);
inputSystem?.UnregisterHandler<IMixedRealityInputHandler>(this);
inputSystem?.UnregisterHandler<IMixedRealityInputHandler<float>>(this);
}
}
public void OnInputChanged(InputEventData<Vector2> ed)
{
Debug.Log("InputChanged");
if (ed.MixedRealityInputAction == DPad)
{
Debug.Log("Touched Touchpad at:" + ed.InputData.x.ToString() + "," + ed.InputData.y.ToString());
}
if (ed.MixedRealityInputAction == Joystick)
{
Debug.Log("Touched Joystick at:" + ed.InputData.x.ToString() + "," + ed.InputData.y.ToString());
}
}
public void OnInputChanged(InputEventData ed)
{
if (ed.MixedRealityInputAction == MenuPressed)
{
Debug.Log("Menu button pressed");
//ActionText.text = "Grab pressed";
}
}
public void OnInputChanged(InputEventData<float> ed)
{
Debug.Log("Float Changed");
if (ed.MixedRealityInputAction == Grip)
Debug.Log("Grab Pressed");
}
Ok... I got this..... It's a BUG in MRTK.
I have 18 actions defined (when you have DPad left, up, down etc it goes by fast!).
When I try to assign my MixedRealityInputAction to an InputAction in the Inspector, when I try to select GrabPressed (which is the 18th action), I get
IndexOutOfRangeException: Index was outside the bounds of the array.
Microsoft.MixedReality.Toolkit.Input.Editor.InputActionPropertyDrawer.OnGUI
Therefore the action does not work....
I tried to use another InputAction instead and assign it to the grab button (I used Select in this case) and it works like a charm....
Beware....
Try
Input.GetAxisRaw(ControllerMappingLibrary.AXIS_11) > 0 || Input.GetAxisRaw(ControllerMappingLibrary.AXIS_12) > 0;
AXIS_11 is grip press for the left controller while 12 is for the right controller
I know one can dynamically change the content of a Label by using an Event Handler, and overriding for example the onRender method, for example:
#Override
public void onRender(ILabelInstance label, IReportContext reportContext)
throws ScriptException {
label.setText("My text!!");
}
But it doesn't seem to work if the label has its Localization TextKey set.
Does anyone already tried to implement it?
Ok ... found a workaround, just remove the Text Key before setting your own text:
#Override
public void onRender(ILabelInstance label, IReportContext reportContext)
throws ScriptException {
label.setTextKey("");
label.setText("My text!!");
}
I'm trying to invoke messagedialog from setting flyout for my Windows 8 Metro app but it's causing the message dialog to flicker. Below is the code.
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SettingsPane.GetForCurrentView().CommandsRequested+=settings_CommandsRequested;
}
private void Settings_CommandsRequested(SettingsPane sender, SetttingsPaneCommandsRequestedEventArgs args)
{
SettingsCommand cmd = new SettingsCommand("test","test1232",new UICommandInvokedHandler(CreateDialog));
args.Request.ApplicationCommands.Add(cmd);
}
private void CreateDialog(IUICommand command)
{
if (ReferenceEquals(command.Id, "cmd"))
{
MessageDialog md = new MessageDialog("Hi");
md.ShowAsync();
}
}
I've contacted official microsoft dev-support and their response was:
"MessageDialog is not recommended within the SettingsFlyout".
So in case you want to implement something simillar to support user's decision from the SettingsPane, you should either:
1) Enable toggling feature in the Flyout.
2) Desiging the SettingsFlyout so it lets the user make decision (for example in Logout cases, add Yes/no buttons inside the settingsFlyout) - Thats the option I chose.
I want to write the contents of a per occasion active TextBox back to the bound property of the ViewModel when the user presses the key combination for save (Ctrl-S).
My Problem with it is, that I'm not able to trigger the execution of the binding so that the bound Text-Property reflects the contents of the TextBox.
-There seems to be no GetBinding-method. Therefore I can not get the Binding and execute it manualy.
-There is no Validate-method such as in WinForms which executes the Binding
-Giving focus to another control from within KeyDown seems not to work, the binding does not execute
How can I achieve this?
Take a look at Aaron's discussion about this in his WiredPrarie blog post : http://www.wiredprairie.us/blog/index.php/archives/1701
I think I understand your question better now. One way around this would be to use a sub-classed textbox with a new property like this from here:
public class BindableTextBox : TextBox
{
public string BindableText
{
get { return (string)GetValue(BindableTextProperty); }
set { SetValue(BindableTextProperty, value); }
}
// Using a DependencyProperty as the backing store for BindableText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BindableTextProperty =
DependencyProperty.Register("BindableText", typeof(string), typeof(BindableTextBox), new PropertyMetadata("", OnBindableTextChanged));
private static void OnBindableTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs eventArgs)
{
((BindableTextBox)sender).OnBindableTextChanged((string)eventArgs.OldValue, (string)eventArgs.NewValue);
}
public BindableTextBox()
{
TextChanged += BindableTextBox_TextChanged;
}
private void OnBindableTextChanged(string oldValue, string newValue)
{
Text = newValue ? ? string.Empty; // null is not allowed as value!
}
private void BindableTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
BindableText = Text;
}
}
Then bind to the BindableText property.
Solution for command-instances
Here a solution I have found which is relatively leightweight, but also a bit "hackish":
btn.Focus(Windows.UI.Xaml.FocusState.Programmatic);
Dispatcher.ProcessEvent(CoreProcessEventsOption.ProcessAllIfPresent);
btn.Command.Execute(null);
First I give the focus to another control (In my case the button which has the bound command). Then I give the system time to execute the bindings and in the end I raise the command which is bound to the button.
Solution without bound commands
Give the Focus to another control and call the Dispatcher.ProcessEvent(...).
anotherControl.Focus(Windows.UI.Xaml.FocusState.Programmatic);
Dispatcher.ProcessEvent(CoreProcessEventsOption.ProcessAllIfPresent);
// Do your action here, the bound Text-property (or every other bound property) is now ready, binding has been executed
Please see also the solution of BStateham.
It's another way to solve the problem
Parts of our UI uses IObservableElementEnumerable.EnumerableChanged in order to update if the user e.g. deletes a domain object from a folder.
When the UI is disposed, we unsubscribe from the event... or so we thought. It turns out that the unsubscribe doesn't have any effect, and our event handler is still called. This caused a number of odd bugs, but also leads to memory leaks.
The only time unsubscription works, is if we store the IObservableElementEnumerable reference instead of calling IObservableElementEnumerableFactory.GetEnumerable(obj) again. But this, in turn, is likely to keep a live reference to the folder object, which will break if the folder itself is deleted by the user.
This is particularly puzzling as the GetEnumerable() documentation clearly states: "It is expected that subsequent calls with the same domain object will yield the same instance of IObservableElementEnumerable." Is this not to be interpreted as a guarantee?
Should there be any reason for unsubscription not working?
The following code replicates the issue on Petrel 2011 (add to a simple plugin with a menu extension, or get the full solution here (DropBox)):
using System;
using System.Linq;
using System.Windows.Forms;
using Slb.Ocean.Core;
using Slb.Ocean.Petrel;
using Slb.Ocean.Petrel.Basics;
using Slb.Ocean.Petrel.UI;
namespace ObservableElementEnumerable
{
public class OEEForm : Form
{
private Droid _droid;
private bool _disposed;
public OEEForm()
{
IInput input = PetrelProject.Inputs;
IIdentifiable selected = input.GetSelected<object>().FirstOrDefault() as IIdentifiable;
if (selected == null)
{
PetrelLogger.InfoOutputWindow("Select a folder first");
return;
}
_droid = selected.Droid;
GetEnumerable().EnumerableChanged += enumerable_EnumerableChanged;
PetrelLogger.InfoOutputWindow("Enumerable subscribed");
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && !_disposed)
{
GetEnumerable().EnumerableChanged -= enumerable_EnumerableChanged;
PetrelLogger.InfoOutputWindow("Enumerable unsubscribed (?)");
_droid = null;
_disposed = true;
}
}
IObservableElementEnumerable GetEnumerable()
{
if (_disposed)
throw new ObjectDisposedException("OEEForm");
object obj = DataManager.Resolve(_droid);
IObservableElementEnumerableFactory factory = CoreSystem.GetService<IObservableElementEnumerableFactory>(obj);
IObservableElementEnumerable enumerable = factory.GetEnumerable(obj);
return enumerable;
}
void enumerable_EnumerableChanged(object sender, ElementEnumerableChangeEventArgs e)
{
PetrelLogger.InfoOutputWindow("Enumerable changed");
if (_disposed)
PetrelLogger.InfoOutputWindow("... but I am disposed and unsubscribed!");
}
}
public static class Menu1
{
public static void OEEBegin1_ToolClick(object sender, System.EventArgs e)
{
OEEForm f = new OEEForm();
f.Show();
}
}
}
To replicate:
Run Petrel with the plugin
Load a project with a folder with objects
Select the folder
Activate the plugin menu item
With the popup open, delete an object in the folder
Close the Form popping up
Delete an object in the folder
The message log should clearly show that the event handler is still called after the form is disposed.
You already keep a reference to the underlying enumerable by connecting the event. Events are references as well. Just keep a reference to the enumerable and unsubscribe from the same instance as the one you subscribe to.
To deal with the issue of objects that are deleted by the user you need to listen to the delete event.