I have a FlyoutMenu in my uwp app. It works fine, but I want to add tooltips to some items. Can I do that?
If you are not working on uwp, the related question here may be of interest:
Showing a tooltip for a MenuItem
class WindowsMenuFlyoutItem: Windows.UI.Xaml.Controls.MenuFlyoutItem
{
public ICommonMenuItem InnerItem { get; set; }
public WindowsMenuFlyoutItem (MyModelObject inner) {
this.Text = inner.GetTitle().Text;
this.Tapped += WindowsMenuFlyoutItem_Tapped;
// set tooltip?
}
private void WindowsMenuFlyoutItem_Tapped(Object sender,
Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
// handler here . . .
}
}
Try this:
private void WindowsMenuFlyoutItem_Tapped(object sender, TappedRoutedEventArgs e)
{
MenuFlyoutItem item = sender as MenuFlyoutItem;
ToolTipService.SetToolTip(item, "tooltip...");
}
Or if you want to set it immediately before the item is tapped:
public class WindowsMenuFlyoutItem : Windows.UI.Xaml.Controls.MenuFlyoutItem
{
public ICommonMenuItem InnerItem { get; set; }
public WindowsMenuFlyoutItem(MyModelObject inner)
{
this.Text = inner.GetTitle().Text;
this.Tapped += WindowsMenuFlyoutItem_Tapped;
Windows.UI.Xaml.Controls.ToolTipService.SetToolTip(this, "tooltip...");
}
}
Related
I have created a custom menu item which appears in the default menu which pops up when selecting text on my custom WebView.
On clicking on the menu item it calls EvaluateJavascript to get the selected WebView text, and then passes the text to another page.
However after performing this action once or twice, some text on certain areas of the screen start to become unresponsive to clicks eg. text on the parts of the WebView become unselectable, clicks on that part of the screen on other pages becomes unresponsive and even the soft keyboard becomes unclickable in some spots. If this continues for a while sometimes my app will then suddenly freeze the entire operating system and I have to soft reset my phone. It appears that there maybe some serious memory leakage going on.
I create my custom menu item in the MainActivity class:
public override void OnActionModeStarted(ActionMode mode)
{
if (Root.IsCurrentPageType<DictPage>() && DictP.IsWebViewFocused())
{
IMenu menu = mode.Menu;
menu.Add("To Notes");
menu.GetItem(0).SetOnMenuItemClickListener(new MyMenuItemOnMenuItemClickListener(this, mode));
}
base.OnActionModeStarted(mode);
}
It is then handled in the Listener class...
public class MyMenuItemOnMenuItemClickListener : Java.Lang.Object, IMenuItemOnMenuItemClickListener
{
private MainActivity mContext;
ActionMode _mode;
public MyMenuItemOnMenuItemClickListener(MainActivity activity, ActionMode mode)
{
this.mContext = activity;
_mode = mode;
}
public bool OnMenuItemClick(IMenuItem item)
{
WEB.CopyToMainNotes();
Device.BeginInvokeOnMainThread(() =>
{
//close menu if clicked
_mode?.Finish();
});
return true;
}
}
...which calls CopyToMainNotes on my derived WebView class and its associated Renderer and EventHandler classes:
public class WebViewEx : Xamarin.Forms.WebView
{
public static WebViewEx WEB;
//Namespace
//YourClass
public event WebViewExEventHandler CallNativeMethodEvent;
public void CallNativeMethod(WebViewExEventType type)
{
WebViewExEventArgs e = new WebViewExEventArgs();
e.EventType = type;
CallNativeMethodEvent?.Invoke(this, e);
}
public WebViewEx()
{
WEB = this;
}
public void CopyToMainNotes()
{
Device.BeginInvokeOnMainThread(() =>
{
CallNativeMethod(WebViewExEventType.copyToMainNotes);
});
}
}
public delegate void WebViewExEventHandler(object sender, WebViewExEventArgs e);
public class WebViewExEventArgs : EventArgs
{
public enum WebViewExEventType { copyToMainNotes };
public WebViewExEventType EventType = WebViewExEventType.copyToMainNotes;
public WebViewExEventArgs() : base()
{
}
}
public class WebViewExRenderer : WebViewRenderer
{
public WebViewExRenderer(Android.Content.Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
WebViewEx ex = e.NewElement as WebViewEx;
ex.CallNativeMethodEvent += WebViewEx_CallNativeMethodEvent;
}
}
internal class JavascriptCallback : Java.Lang.Object, IValueCallback
{
public JavascriptCallback(Action<string> callback)
{
_callback = callback;
}
private Action<string> _callback;
public void OnReceiveValue(Java.Lang.Object value)
{
_callback?.Invoke(Convert.ToString(value));
}
}
private void WebViewEx_CallNativeMethodEvent(object sender, WebViewExEventArgs e)
{
switch (e.EventType)
{
case WebViewExEventType.copyToMainNotes:
{
CopyToMainNotes();
break;
}
}
}
public void CopyToMainNotes()
{
string script = "(function(){ return window.getSelection().toString()})()";
var response = string.Empty;
Control?.EvaluateJavascript(script, new JavascriptCallback((r) =>
{
response = r;
Device.BeginInvokeOnMainThread(() =>
{
DPage.CopyThisTextToAnotherPage(response.ToString().Trim('\"'));
});
}));
}
}
The CopyToMainNotes method above is where the EvaluateJavascript takes place and the selected text finally gets sent to another page.
Any ideas where I might be going wrong here? Thanks in advance!
So i was wondering how i would make some kind of superwindow/superclass for my windows. The windows look the following:
The first picture shows how every single one of the windows would potentially look when pressed on one of the buttons. So each of these windows would have the same basic functionality but with some different returning objects which can be seen in the following code.
public partial class FlatsteelWindow : Window {
private string str;
private IList test;
public FlatSteel _returnObject { get; set; }
public double amount { get; set; }
public FlatsteelWindow(object inputObject) {
InitializeComponent();
_returnObject = inputObject as FlatSteel;
FetchList();
}
private void FetchList() {
test = MaterialLogic.GetFlatsteelList() as List<FlatSteel>;
foreach (FlatSteel flatSteel in test) {
flatsteelListView.Items.Add(flatSteel.Name);
}
}
private void flatsteelListView_PreviewMouseLeftButtonUp(object sender, RoutedEventArgs e) {
var item = (sender as ListView).SelectedItem;
if (item != null) {
_returnObject = flatsteelListView.SelectedItems as FlatSteel;
str = item.ToString();
amountTextbox.IsEnabled = true;
FindObject(str);
}
}
private void FindObject(string s) {
foreach (FlatSteel flatSteel in test) {
if (s.Equals(flatSteel.Name)) {
_returnObject = flatSteel;
}
}
}
private void submitButton_Click(object sender, RoutedEventArgs e) {
if (!string.IsNullOrEmpty(amountTextbox.Text)) {
amount = amountTextbox.Text.customParseToDouble();
this.Close();
}
else {
MessageBox.Show("Indtast venligst en værdi.");
}
}
}
This is the code for the shown Flatsteel window.
Here is the code which creates the window:
private void flatsteelButton_Click(object sender, RoutedEventArgs e) {
FlatsteelWindow flatsteelWindow = new FlatsteelWindow(this);
flatsteelWindow.ShowDialog();
test = flatsteelWindow._returnObject;
amount = flatsteelWindow.amount;
if (test != null && amount != null) {
AddToGridView();
}
}
public FlatSteel test { get; set; }
private double amount { get; set; }
As we can see in this code the flatsteel object is needed where this method is invoked. The 6 different windows, which each are represented by the six buttons on the 2nd picture, each return an object to their type of material. So to make it clear what my question is:
How do i make one superclass/superwindow that has all the same basic funtionality, from which i then can inherit from and add the needed functionality for the different returning objects?
I created a ListBoxItem where I have a property Name and override ToString() to give back name. That works nicely when I add new items.
But now I need to force the ListBox to update the labels when I change the name of my ship. I thought Refresh or Update would do that but that doesn't work.
I might be missing something very easy here.
public class ShipListBoxItem
{
public ListBox Parent { get; set; }
public ShipType Ship { get; set; }
public ShipListBoxItem()
{
Ship = new ShipType();
}
public ShipListBoxItem(ShipType st)
{
Ship = st;
}
public override string ToString()
{
return Ship.Name;
}
public void UpdateListBox()
{
Parent.Refresh(); //My problem is here. Update doesn't work either.
}
public static ShipListBoxItem AddToListBox(ListBox lb, ShipType ship)
{
ShipListBoxItem li = new ShipListBoxItem(ship);
li.Parent = lb;
lb.Items.Add(li);
return li;
}
}
If you use a List<T> as the DataSource for the listbox it is pretty easy to have changes to items show up. It also means there is no real reason to have a special class for adding a ShipListBoxItem to a ListBox, your basic Ship class may work:
class ShipItem
{
public enum ShipTypes { BattleShip, Carrier, Destroyer, Submarine, Frigate };
public ShipTypes Ship { get; set; }
public string Name { get; set; }
public ShipItem(string n, ShipTypes st)
{
Name = n;
Ship = st;
}
public override string ToString()
{
return String.Format("{0}: {1}", Ship.ToString(), Name);
}
}
The form related stuff:
private void Form1_Load(object sender, EventArgs e)
{
// add some ships
Ships = new List<ShipItem>();
Ships.Add(new ShipItem("USS Missouri", ShipTypes.BattleShip));
Ships.Add(new ShipItem("USS Ronald Reagan", ShipTypes.Carrier));
lb.DataSource = Ships;
}
private void button1_Click(object sender, EventArgs e)
{
// change a ship name
lb.DataSource = null; // suspend binding
this.Ships[0].Name = "USS Iowa";
lb.DataSource = Ships; // rebind
lb.Refresh();
}
As an alternative, you can also tell the Listbox to use a specific property for the display using DisplayMember:
lb.DataSource = Ships;
lb.DisplayMember = "Name";
This would use the Name property in the listbox instead of the ToString method. If your list is changing a lot, use a BindingList instead. It will allow changes to the list show up in the ListBox as you add them without toggling the DataSource.
Try this
ListBox.RefreshItems()
msdn
EDIT: You can use an extended class like this:
public class FooLisBox : System.Windows.Forms.ListBox
{
public void RefreshAllItems()
{
RefreshItems();
}
}
private void button1_Click(object sender, EventArgs e)
{
(listBox1.Items[0] as ShipListBoxItem).Ship.Name = "AAAA";
listBox1.RefreshAllItems();
}
I managed to solve my problem.
Mostly, thanks Jose M.
I ran into a problem however. RefreshItems() triggers OnSelectedIndexChanged()
so my overridden class looks like this
public class MyListBox : ListBox
{
public bool DoEvents{ get; set; } // Made it public so in the future I can block event triggering externally
public MyListBox()
{
DoEvents = true;
}
public void RefreshAllItems()
{
SuspendLayout();
DoEvents = false;
base.RefreshItems(); // this triggers OnSelectedIndexChanged as it selects the selected item again
DoEvents = true;
ResumeLayout();
}
// I only use this event but you can add all events you need to block
protected override void OnSelectedIndexChanged(EventArgs e)
{
if (DoEvents)
base.OnSelectedIndexChanged(e);
}
}
I can't clear ListBox.SelectedItems collection. Please suggest what I'm doing wrong.
I clear collection in different ways, but it leave previous collection.
I clear collection so:
chListBox.SelectedItems.Clear();
or
chListBox.UnselectAll();
or
chListBox.SetSelectedItems(new ArrayList());
My code:
public class CheckListBox : ListBox
{
public CheckListBox()
{
this.SelectionChanged += CheckListBox_SelectionChanged;
this.Resources = Application.LoadComponent(new Uri("/TASWpfControls;component/Resources/CheckListBoxResources.xaml", UriKind.RelativeOrAbsolute)) as ResourceDictionary;
this.ItemContainerStyle = this.Resources["CheckListBoxItem"] as Style;
this.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ClickEventHandler));
}
private void ClickEventHandler(object sender, RoutedEventArgs routedEventArgs)
{
RoutedEventArgs eventArgs = new RoutedEventArgs(ItemSelectedEvent);
this.RaiseEvent(eventArgs);
}
public string PropertyName { get; set; }
public string PropertyCompare { get; set; }
public static readonly DependencyProperty SelectedSourceProperty =
DependencyProperty.Register("SelectedSource", typeof(IList), typeof(CheckListBox), new PropertyMetadata(SelectedSourceChanged));
public IList SelectedSource
{
get { return (IList)GetValue(SelectedSourceProperty); }
set { SetValue(SelectedSourceProperty, value); }
}
public static RoutedEvent ItemSelectedEvent =
EventManager.RegisterRoutedEvent("ItemSelected", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(CheckListBox));
public event RoutedEventHandler ItemSelected
{
add { AddHandler(ItemSelectedEvent, value); }
remove { RemoveHandler(ItemSelectedEvent, value); }
}
protected static void SelectedSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CheckListBox chListBox = d as CheckListBox;
if (chListBox != null)
chListBox.SortListBox(chListBox, e.NewValue);
}
protected virtual void SortListBox(CheckListBox chListBox, object newValue)
{
chListBox.SelectedSource = newValue as IList;
IList selectedItems = chListBox.SelectedSource;
chListBox.SelectedItems.Clear();
if (selectedItems != null && selectedItems.Count > 0)
{
foreach (object selectedItem in selectedItems)
{
foreach (object item in chListBox.Items)
{
if (eIReflector.GetValue(item, chListBox.PropertyName).Equals(eIReflector.GetValue(selectedItem, chListBox.PropertyName)))
chListBox.SelectedItems.Add(item);
}
}
}
}
protected virtual void CheckListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (object addedItem in e.AddedItems)
{
if (!SelectedItems.Contains(addedItem))
SelectedItems.Add(addedItem);
if (!SelectedSource.Contains(addedItem))
SelectedSource.Add(addedItem);
}
foreach (object removedItem in e.RemovedItems)
{
this.SelectedItems.Add(removedItem);
this.SelectedSource.Remove(removedItem);
}
}
}
Try using
chListBox.ClearSelection(); // For C#
and
chListBox.UnselectAll(); // For WPF
It is working perfect for me...
I was able to do it in WPF doing the following:
while(chListBox.SelectedItems.Count > 0)
{
chListBox.Items.Remove(chListBox.SelectedItem);
}
I was able to get it working with the following:
var listboxItem = m_listBoxAutocomplete.FindChildElements<ListBoxItem>().FirstOrDefault(t => t.IsSelected);
Keyboard.Focus(listboxItem);
However you will need to google FindChildElements extention which is readily available
I have many controls in a window. Requirement is to know which control gets the focus from the lost focus event of a control.
Say, A Text box and it has the focus. Now I am clicking a button. while doing this, need to know that i am moving the focus to button from the Text box lost focus event.
So how could i achieve this..
This is what I did and its working for me
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
lostFocusControl = e.OldFocus;
}
private void PauseBttn_PreviewKeyDown(object sender, KeyEventArgs e)
{
/**invoke OnPreviewLostKeyboardFocus handller**/
}
Hope it will help
You can use FocusManager to handle this,
In your LostFocusEvent, Use FocusManager.GetFocusedElement()
uiElement.LostFocus+=(o,e)=>
{
var foo=FocusManager.GetFocusedElement();
}
The following class watches the FocusManager for changes in focus, it's a looped thread so you have to put up with the fact that it's running but when focus changes it will just raise an event letting you know what changed.
Just add these two classes to your project.
public class FocusNotifierEventArgs : EventArgs
{
public object OldObject { get; set; }
public object NewObject { get; set; }
}
public class FocusNotifier : IDisposable
{
public event EventHandler<FocusNotifierEventArgs> OnFocusChanged;
bool isDisposed;
Thread focusWatcher;
Dispatcher dispatcher;
DependencyObject inputScope;
int tickInterval;
public FocusNotifier(DependencyObject inputScope, int tickInterval = 10)
{
this.dispatcher = inputScope.Dispatcher;
this.inputScope = inputScope;
this.tickInterval = tickInterval;
focusWatcher = new Thread(new ThreadStart(FocusWatcherLoop))
{
Priority = ThreadPriority.BelowNormal,
Name = "FocusWatcher"
};
focusWatcher.Start();
}
IInputElement getCurrentFocus()
{
IInputElement results = null;
Monitor.Enter(focusWatcher);
dispatcher.BeginInvoke(new Action(() =>
{
Monitor.Enter(focusWatcher);
results = FocusManager.GetFocusedElement(inputScope);
Monitor.Pulse(focusWatcher);
Monitor.Exit(focusWatcher);
}));
Monitor.Wait(focusWatcher);
Monitor.Exit(focusWatcher);
return results;
}
void FocusWatcherLoop()
{
object oldObject = null;
while (!isDisposed)
{
var currentFocus = getCurrentFocus();
if (currentFocus != null)
{
if (OnFocusChanged != null)
dispatcher.BeginInvoke(OnFocusChanged, new object[]{ this, new FocusNotifierEventArgs()
{
OldObject = oldObject,
NewObject = currentFocus
}});
oldObject = currentFocus;
}
}
Thread.Sleep(tickInterval);
}
}
public void Dispose()
{
if (!isDisposed)
{
isDisposed = true;
}
}
}
Then in your code behind, create a new instance of the Focus Notifier class and hook on to it's OnFocusChanged event, remember to dispose it at the end or the thread will keep your app open.
public partial class MainWindow : Window
{
FocusNotifier focusNotifier;
public MainWindow()
{
InitializeComponent();
focusNotifier = new FocusNotifier(this);
focusNotifier.OnFocusChanged += focusNotifier_OnFocusChanged;
}
void focusNotifier_OnFocusChanged(object sender, FocusNotifierEventArgs e)
{
System.Diagnostics.Debug.WriteLine(e.OldObject);
System.Diagnostics.Debug.WriteLine(e.NewObject);
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
focusNotifier.Dispose();
base.OnClosing(e);
}
}
have you tried to register your controls to Control.LostFocus event and there you can check for Form.ActiveControl, to determine which control currently has the focus