I have a form with a basic overflow menu, that is supposed to offer options of manipulating a list of items of that form. One part is sorting the list by various means (which works well), the other part is providing a filtering of the list. E.g. having a simple shopping list of items that can be checked and I want the filter to show only "open" items, yet unchecked to focus on.
Can I add a previously assembled component to the overflow menu? Its just a dialog spawning in that location, so it should be able to house any component for that matter. However, the options "add(Material)CommandToOverflowMenu" only allow to add commands directly or a string, icon, listener combination.
If a custom component is not possible, I could still use the icon to show an empty checkbox first, and then update to an checked checkbox icon, once pressed. However, how to manipulate an existing overflow item, after it was added? I dont even see a way to remove previously added overflow items, as the "getOverflowCommands" returns an Iterable, which is not supposed to be used for manipulation of the list.
Is there a way to do this, I do I have to setup my own, custom, overflow menu like dialog?
Thanks and best regards
EDIT WITH SOLUTION
I created a new class to solve my problem:
public class ToggleCommand extends Command {
private boolean selected;
private static Image selectedImage = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_CHECKED,
new Label().getUnselectedStyle());
private static Image unSelectedImage = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED,
new Label().getUnselectedStyle());
public ToggleCommand(String command) {
this(command, selectedImage);
}
public ToggleCommand(String command, Image icon) {
super(command, icon);
selected = true;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
if (selected) {
this.setIcon(selectedImage);
} else {
this.setIcon(unSelectedImage);
}
}
}
which you can use like this:
filterDoneCommand = new ToggleCommand("Show closed?") {
#Override
public void actionPerformed(ActionEvent evt) {
filterDoneCommand.setSelected(!filterDoneCommand.isSelected());
// do your other stuff
}
};
getToolbar().addCommandToOverflowMenu(filterDoneCommand);
The overflow menu wasn't built well and should probably be rewritten. We don't have builtin support for checkbox options there and making a change to add it would be pretty cumbersome.
The best workaround I can think of is to add a command with a checkbox as an icon. Then checking/unchecking when the command is invoked. The addMaterialCommandToOverflowMenu and similar command methods return a Command instance which you need to save for future reference in this case.
If you want to go "all out" you can just add a button to the right side toolbar and use that to show a completely customized overflow menu. That might be easier in some regards.
Related
I want to make my app to be accessible (make my app exposed for screen readers, which are UI automation client, like "Narrator").
I got some ContentControl that when it got keyboard focus, I'm showing a tooltip (taking it from the Tooltip property of this ContentControl). It's a control that used in many ways, for example it can be used like that:
the Content of this ContentControl is a question mark icon image, and the ToolTip is the help text...
Here is a concept code:
class AutoTooltipOnFocus : ContentControl
{
public AutoTooltipOnFocus()
{
this.GotKeyboardFocus += OnGotKeyboardFocus;
}
private void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs keyboardFocusChangedEventArgs)
{
bool automationListens = AutomationPeer.ListenerExists(AutomationEvents.AutomationFocusChanged);
if (automationListens)
{
// don't show tooltip because GetNameCore of MyAutoPeer will represent the ToolTip value
return;
}
// show tooltip (by reading the ToolTip value of this AutoTooltipOnFocus)
}
protected override AutomationPeer OnCreateAutomationPeer()
{
return new MyAutoPeer(this);
}
}
class MyAutoPeer : FrameworkElementAutomationPeer
{
private AutoTooltipOnFocus _owner;
public MyAutoPeer(AutoTooltipOnFocus owner)
: base(owner)
{
}
protected override string GetNameCore()
{
return GetToolTipValueFromOwner();
}
private string GetToolTipValueFromOwner()
{
// just for the simplicity of the example, I return this:
return ToolTipService.GetToolTip(_owner).ToString();
}
}
Narrator, for example, reads the textual representation of the Content (let's say the property AutomationProperties.Name of the image is set to "help icon") and then says "Tooltip: some help text".
I don't want to count on the tooltip been read by all the screen readers (correct me if I'm wrong thinking some of them don't read Tooltips), so I made my GetNameCore return the ToolTip content so that I know it would be read necessarily, and I prevented the appearance of the tooltip (at OnGotKeyboardFocus handler) in order to prevent double reading of the same help text.
The problem is that: I thought that asking AutomationPeer.ListenerExists(AutomationEvents.AutomationFocusChanged) tells me that UI automation is listening to my app, but when narrator is not running, this method returns "false" and the rest of the times it returns true, so no tooltip appears when no one using screen reader, so I need to know what is the way to indicate whether UI automation client is running and listening to my app.
Maybe there is a workaround by adding some code to my custom AutomationPeer.
Thanks for giving your time!
I am working with this sample: http://www.arcgis.com/home/item.html?id=ef6a80c07fb84f84a5fe5192221f582c
specifically with “GraphicsDataSourcesDemo”, which uses Silverlight API.
It allow for loading csv file with Lat Long coordinates, which are displayed as points on the map. In my own application I successfully implemented part about displaying points, but I have problem with a Clear button (which is not implemented in the included sample code). Since INotifyCollectionChanged and StreamReader are used, standard method like:
private void GPSClearButton_Click(object sender, RoutedEventArgs e)
{
GraphicsLayer graphicsLayer = Map.Layers["data"] as GraphicsLayer;
graphicsLayer.ClearGraphics();
}
does not work.
I would appreciate any advice on how to make displayed points disappear from my map (after user decide so by clicking a button Clear).
The graphical elements merely represent data items, so either you want to throw away the data items (that's what I understand from an action called 'Clear') or you want to 'hide' the displayed data items and later be able to 'show' them again. Only the latter one is to be solved in the View/GraphicsLayer. The former one means you have to 'Clear' the list of data items i.e. coordinates, and (if used correctly) the bindings from View to the DataContext will ensure that all corresponding visual elements are removed from the UI.
So you will need something like this:
public class CoordinateCollectionViewmodel
{
public ICommand Clear { get; set; } // setup to call Coordinates.Clear()
public ObservableCollection Coordinates { get; set; }
}
I'm using a listbox to display a list of numeric values where the selected value get applied to an object and is saved. I had the idea to set the height on on the listbox to be just enough to display a single row. Of course this causes the vertical scollbar to appear, and without a scroll body, which is exactly what I was looking for.
When I click the up/down arrow on the list box scrollbar, it scrolls the next item into view correctly. However, the item is not selected. I immediately had problems with the actual selected value being saved and not the value that is in view. This is because it requires a click on the number after the scrollbar button to actually select the value, which is not very intuitive.
So, after some intel-sense hunting, I began search for possible ways to increment the selected value with clicks on the listbox scrollbar buttons, but I can't find anything that actually uses them.
I have found posts that describe actions when clicking on the scroll bar body, and still others that use designer added buttons to perform the index change on the listbox. However, the scroll bar body is not visible do to the short height of the list box and it seems silly to add more buttons when I already have those two available and doing most of the work.
I have also found a post that described something similar, but using a listview. But, I would hate to have to swap the control out at this point for one feature I really think should be available somewhere on the control I'm currently using.
So, I guess what I'm looking for is a way to address the click event handler of the vertical scrollbar buttons on a listbox control.
Any help would be greatly appreciated.
(Thanks all, for the 1000 other things I didn't have to post to solve here!)
I had heard about that Phil, and your right. I'm doing a replacement for the numeric up-down.
I just figured that there was probably a viable alternative, since that specific control was not originally part of the framework. I had also gotten much of it working and really like the results, and the way it picked up the theme.
Since the core of this application will become a start point for future applications, I wanted to include this functionality and was prepared to do a little work for it.
What I ended up doing was a little complicated that it was worth, but made easy with a useful helper function. I needed to search the 'Visual Tree' for my target type. From there I was able to access enough functionality to finish up.
First:
Using a helper function I found here (Thanks Bruno) I was able to add this to my Loaded event:
private Double currentVerticalOffset;
private void Page_Loaded_1(object sender, RoutedEventArgs e)
{
ScrollViewer sv = Helpers.ViewHelpers.ListBoxHelper.FindVisualChild<ScrollViewer>(listbox);
sv.ScrollChanged += HandleRankScrollChange;
currentVerticalOffset = sv.VerticalOffset;
}
Then, I handle the scroll changed event:
private void HandleRankScrollChange(object sender, ScrollChangedEventArgs e)
{
ScrollViewer sv = Helpers.ViewHelpers.ListBoxHelper.FindVisualChild<ScrollViewer>(listbox);
if (sv.VerticalOffset > currentVerticalOffset)
{
Helpers.ViewHelpers.ListBoxHelper.SelectNextItem(listbox);
}
if (sv.VerticalOffset < currentVerticalOffset)
{
Helpers.ViewHelpers.ListBoxHelper.SelectPreviousItem(listbox);
}
currentVerticalOffset = sv.VerticalOffset;
}
The helpers I call here are pretty simple, but again, this will become a foundation kit, so having the methods will probably come in handy again.
public static void SelectNextItem(ListBox lb)
{
if (lb.SelectedIndex < lb.Items.Count)
{
lb.SelectedIndex++;
}
}
public static void SelectPreviousItem(ListBox lb)
{
if (lb.SelectedIndex > 0)
{
lb.SelectedIndex--;
}
}
Bruno's helper function
public static childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
{
return (childItem)child;
}
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
{
return childOfChild;
}
}
}
return null;
}
Thanks again.
I'm working on my first project in WPF/XAML, and there's a lot I've not figured out.
My problem is simple - I need a window that has a bunch of fields at the top, with which the user will enter his selection criteria, a retrieve button, and a data grid. When the user clicks on the button, a query is run, and the results are used to populate the grid.
Now the simple and obvious and wrong way to implement this is to have a single module containing a single window, and have everything contained within it - entry fields, data grid, the works. That kind of mangling of responsibilities makes for an unmaintainable mess.
So what I have is a window that is responsible for little more than layout, that contains two user controls - a criteria control that contains the entry fields and the retrieve button, and a data display control that contains the data grid.
The question is how to get the two talking to each other.
Years back, I would have added a function pointer to the criteria control. The window would have set it to point to a function in the display control, and when the button was clicked, it would have called into the display control, passing the selection criteria.
More recently, I would have added an event to the criteria control. I would have had the window set a handler in the display control to listen to the event, and when the button was clicked, it would have raised the event.
Both of these mechanisms would work, in WPF. But neither is very XAMLish. It looks to me like WPF has provided the ICommand interface specifically to accommodate these kinds of connection issues, but I've not yet really figured out how they are intended to work. And none of the examples I've seen seem to fit my simple scenario.
Can anyone give me some advice on how to fit ICommand to this problem? Or direct me to a decent explanation online?
Thanks!
MVVM is the prevalent pattern used with WPF and Silverlight development. You should have a read up on it.
Essentially, you would have a view model that exposes a command to perform the search. That same view model would also expose properties for each of your criteria fields. The view(s) would then bind to the various properties on the view model:
<TextBox Text="{Binding NameCriteria}"/>
...
<Button Command="{Binding SearchCommand}".../>
...
<DataGrid ItemsSource="{Binding Results}"/>
Where your view model would look something like:
public class MyViewModel : ViewModel
{
private readonly ICommand searchCommand;
private string nameCriteria;
public MyViewModel()
{
this.searchCommand = new DelegateCommand(this.OnSearch, this.CanSearch);
}
public ICommand SearchCommand
{
get { return this.searchCommand; }
}
public string NameCriteria
{
get { return this.nameCriteria; }
set
{
if (this.nameCriteria != value)
{
this.nameCriteria = value;
this.OnPropertyChanged(() => this.NameCriteria);
}
}
}
private void OnSearch()
{
// search logic, do in background with BackgroundWorker or TPL, then set Results property when done (omitted for brevity)
}
private bool CanSearch()
{
// whatever pre-conditions to searching you want here
return !string.IsEmpty(this.NameCriteria);
}
}
Assume a standard Desktop Explorer style app:
menu at top
navigation tree on left
item view on right
and everything is neatly separated into Menu, Navigation and Item modules.
The menu has a generic "View" -> "Display mode" menu selection, that changes the current item view between:
"Icon view"
"List view"
"Detail view"
To catch the display type change, I currently publish and subscribe to a DisplayModeChanged event.
First problem:
This menu option should only display when an appropriate view is visible that has the display modes. What is the best way to control shared menu buttons so they only show if at least one relevant view is active?
Second Problem:
If a view comes into existence after the selection was made, how should it pickup the current display mode state from the menu?
My first thought was that you could have solved the first problem by storing item view menu settings somewhere together with your item view. Then, on view activation you would ask your new view about its "custom menu actions" that it wants to show and one of them would be "Display mode" for item view. Other views would provide other menu actions and this one will not be shown.
But this solution contradicts with your second requirement, because you obviously want to have some global 'ItemViewDisplayModeSetting', and whenever it is changed you want all item views to be notified and change their display mode together.
So, let's just solve it right by applying the dependency injection principle. Do not look for things, ask for things. Your menu presenter (view model) requires some service that knows whether there are active item views or not. Your item view presenter requires a service that will provide initial display mode and will notify about its changes. We end up with this code:
interface IActiveViewsService : INotifyPropertyChanged
{
bool HasActiveViewsSupportingDisplayMode { get; }
}
interface IDisplayModeService : INotifyPropertyChanged
{
DisplayMode DisplayMode { get; }
}
//your item view presenter (view model)
class ItemViewModel
{
public ItemViewModel(IDisplayModeService displayModeService)
{
//obtain initial setting
//subscribe to property changes
}
}
//your menu presenter (view model)
class MenuViewModel
{
public MenuViewModel(IActiveViewsService activeViewsService)
{
//obtain initial setting
//subscribe to property changes
}
}
Then you need some way to edit your Diplay Mode from menu... you may combine that together into IDisplayModeService or you may create a new interface for that. The implementation will be a simple class that holds one DisplayMode instance and you inject one instance of that class into all your menus and item views. You'll also need to implement IActiveViewsService, this one will probably wrap IRegionManager from PRISM or whatever is your UI management mechanism... it will listen for region changes and react when a new item view is created or when there is no one left.
interface IDisplayModeEditor
{
void ChangeDisplayMode(DisplayMode newMode);
}
//your final menu presenter (view model)
class MenuViewModel
{
public MenuViewModel(IActiveViewsService activeViewsService, IDisplayModeEditor modeEditor)
{
//obtain initial setting
//subscribe to property changes
}
}
//your final menu presenter (view model)
class DisplayModeStorage : IDisplayModeService, IDisplayModeEditor
{
private DisplayMode displayMode;
public DisplayMode DisplayMode
{
get { return this.displayMode; }
//standard propertychange notification
set
{
if(value == this.displayMode)
return;
this.displayMode = value;
this.RaisePropertyChanged("DisplayMode");
}
}
public void ChangeDisplayMode(DisplayMode newMode)
{
this.DisplayMode = newMode;
}
}
HTH.