I'm trying to binding a SL BusyIndicator to a collection of busy messages. When the collection has items, the indicator will display the messages. When the collection of messages is empty the indicator will hide.
First off the indicator is not displaying my messages, all I see is a blank indicator box, with an indeterminate progress bar:
<UserControl.Resources>
...
<anotherAssembly:CollectionToBoolConverter x:Key="CollectionToBoolConverter" />
<DataTemplate x:Key="LoadingMessageDataTemplate">
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding AllocationLoadingMessages}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
...
</UserControl.Resources>
...
<controlToolkit:BusyIndicator
IsBusy="{Binding AllocationLoadingMessages, Converter={StaticResource CollectionToBoolConverter}}"
BusyContent="{Binding AllocationLoadingMessages}"
BusyContentTemplate="{StaticResource LoadingMessageDataTemplate}"/>
///content
</controlToolkit:BusyIndicator>
...
ViewModel:
private ObservableCollection<string> _allocationLoadingMessages = new ObservableCollection<string>();
public ObservableCollection<string> AllocationLoadingMessages
{
get { return _allocationLoadingMessages; }
set
{
SetValue(ref _allocationLoadingMessages, value, "AllocationLoadingMessages");
}
}
So how do I get a simple list of messages in my Indiciator?
Thanks,
Mark
Your code is very near the target, all you need to do to get this to work is to replace the line
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding AllocationLoadingMessages}" >
with
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding}" >
I tested this both in Silverlight 3 and 5, so I figure it'll work in 4 as well.
I'm not sure what your set { } code for the AllocationLoadingMessages property is for; you shouldn't have to replace the collection every time you make a change to it. When I got it to work, all I did in the view model was this:
public ObservableCollection<string> AllocationLoadingMessages { get; set; }
private int _loadingMessageNumber = 0;
private void Add()
{
AllocationLoadingMessages.Add( "Loading message #" + ++_loadingMessageNumber );
PropertyChanged( this, new PropertyChangedEventArgs( "AllocationLoadingMessages" ) );
}
private void RemoveFirst()
{
if( AllocationLoadingMessages.Count > 0 )
{
AllocationLoadingMessages.RemoveAt( 0 );
PropertyChanged( this, new PropertyChangedEventArgs( "AllocationLoadingMessages" ) );
}
}
I'm sorry I'm late (didn't come across this question until now), but I hope it can at least help others who are looking for the answer to this.
Related
Can someone please tell me what I'm doing wrong? The RefreshView was just working yesterday and today I can't get it to work in any page. I created a brand new page with just a RefreshView and when I try to pull down it doesn't budge. Doesn't pull down, doesn't refresh, nothing. It was just working last night and today after no code changes it's not working. I've tried on the simulator and on my actual iPad. Before anyone suggests, there are no updates to any of my NuGet packages and I can't find any reference to this issue on Google.
XF: v5.0.0.2244
View:
<?xml version="1.0" encoding="utf-8" ?>
<views:MvxContentPage
x:Class="MyApp.UI.Pages.PricingPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:MyApp.Core.ViewModels;assembly=MyApp.Core"
xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
Title="{Binding Title}"
x:DataType="viewModels:PricingViewModel"
x:TypeArguments="viewModels:PricingViewModel">
<views:MvxContentPage.Content>
<RefreshView Command="{Binding RefreshCommand}" IsRefreshing="{Binding IsRefreshing}">
<CollectionView ItemsSource="{Binding MenuItems}" />
</RefreshView>
</views:MvxContentPage.Content>
</views:MvxContentPage>
ViewModel:
using System.Threading;
using System.Threading.Tasks;
using MyApp.Core.ChurromigosApi;
using MyApp.Core.Services;
using MyApp.Core.ViewModels.Base;
using MvvmCross.Commands;
using MvvmCross.ViewModels;
namespace MyApp.Core.ViewModels
{
public class PricingViewModel : BaseViewModel
{
private readonly IMenuItemService menuItemService;
public PricingViewModel(IMenuItemService menuItemService)
{
this.menuItemService = menuItemService;
this.RefreshCommand = new MvxAsyncCommand(this.Refresh);
this.MenuItems = new MvxObservableCollection<MenuItem>();
this.Title = "Pricing";
}
public MvxObservableCollection<MenuItem> MenuItems { get; set; }
public IMvxAsyncCommand RefreshCommand { get; }
public bool IsRefreshing { get; set; }
public override Task Initialize()
{
this.IsRefreshing = true;
return Task.CompletedTask;
}
private async Task Refresh()
{
var allMenuItems = await this.menuItemService.GetMenuItems(CancellationToken.None);
this.MenuItems.Clear();
this.MenuItems.AddRange(allMenuItems);
}
}
}
I found a similar problem with RefreshView, though in my case instead of becoming unresponsive, the app just crashes.
It appears that we need to pay more attention to the binding Mode for the property used for the IsRefreshing indicator:
If we want the RefreshView to automatically trigger the command when the VM's binded property is changed to True, then don't include a Mode, or use Mode=TwoWay, but don't manually change the value or you'll be stuck in a loop (or include a if(IsRefreshing) return; in your command or you wont get any results)
If we want to manually set the VM's property in the backend to show/hide the IsRefreshing activity indicator without triggering the command, then use Mode=OneWay or else you will be stuck in a loop.
In your particular case it could be related to the fact that you're setting your IsRefreshing property to true, and then you never set it back to false? I would wrap your Refreshing() method's code with a try, setting IsRefreshing=false at the beginning and then =false in a finally block.
I have created ContenPage with WebView inside xaml (code below). I'am passing webpageURL in constructor. My target is to add item to webview localstorage using javascirpt, before even starting loading passed URL. I tried several methods like
Browser.EvaluateJavaScriptAsync(javascirpt);
or
Browser.Evaluate(javascirpt);
before executing in Constructor
Browser.Source = URL;
or wrapping this methods inside
Device.BeginInvokeOnMainThread(async () =>{});
but still they didn't work as supposed. Do you know any solutions to my problem ?
public partial class WebView : ContentPage
{
public WebView(string URL)
{
string item = "test";
string javascirpt = String.Format("localStorage.setItem('ls.item', '{0}')", item);
Browser.Eval(javascirpt);
Browser.Source = URL;
}
}
Xaml:
<WebView x:Name="Browser" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"
HeightRequest="1000"
WidthRequest="1000"
Navigating="WebOnNavigating"
Navigated="WebOnEndNavigating" />
We don't recommend that put these code in the constructor .Because the class "WebView" hasn't initialized yet when you call these code.Try to move them to method OnAppearing.
protected override void OnAppearing()
{
base.OnAppearing();
string item = "test";
string javascirpt = String.Format("localStorage.setItem('ls.item', '{0}')", item);
Browser.Eval(javascirpt);
Browser.Source = URL;
}
I have a use case where I plan to have a rich:pickList so that a user can pick multiple items from a list. But, in few cases the number of items in pickList are too large(in hunderds and sometimes in thousands) and it is not practical for the user to scroll the whole length down. So, is there a way i can have a search box above the lefthand side list of the pickList? It should be similar to that of the autoComplete component so that there are always 10 items in the pickList, which are the closest matches of the keywords entered in the search box.
I use richfaces 4 and JSF 2.0.
What about this?
Page:
<h:inputText value="#{bean.prefix}">
<a4j:ajax event="keyup" render="list" />
<a4j:ajax event="focus" oncomplete="saveList()" />
</h:inputText>
<a4j:region>
<rich:pickList id="list" value="#{bean.selectedList}">
<f:selectItems value="#{bean.filteredList}" var="str" itemLabel="#{str}" itemValue="#{str}" />
</rich:pickList>
</a4j:region>
<a4j:jsFunction name="saveList" execute="list" />
Bean:
// fullList - contains everything
public List<String> getFilteredList() {
List<String> filteredList = new ArrayList<String>();
Set<String> filteredSet = new TreeSet<String>();
for (String s : fullList) {
if (s.startsWith(getPrefix())) {
filteredSet.add(s);
}
if (filterSet.size() > 9) {
break;
}
}
filteredSet.addAll(selectedList);
// we need to add the selected items so they would be rendered
// target list needs to be a subset of the source list
filteredList.addAll(filteredSet);
return filteredList;
}
I'm using Caliburn Micro MVVM. I want to make category selection usercontrol consisting of few dynamic comboboxes (or listboxes) based on generic tree collection. User must choose any leaf node from category tree, so new collections will keep appearing as long as selected node has children beneath it. Depth may vary.
I want it to look like this: http://i.imgur.com/c2uzv.png
...and so far it looks like this:
CategorySelectorModel.cs:
public BindableCollection<BindableCollection<Category>> Comboboxes { get; set; }
CategorySelector.xaml:
<ItemsControl x:Name="Comboboxes">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding}" DisplayMemberPath="Name"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
So there's my question: Would it be possible to specify an event for each created combobox and access its SelectedItem property?
It was easier than I expected. My question was pretty unfortunate from this point. I started with this:
<ComboBox ItemsSource="{Binding}" DisplayMemberPath="Name" cal:Message.Attach="CategoryChanged($this.SelectedItem)"/>
Every node of my category tree has a Depth property. Since depth of last selected element is related to number of collections, I just used this property to remove all unnecessary collections when any selected item has changed.
public void CategoryChanged(object selected)
{
int depth = 0;
var newcombobox = new BindableCollection<Category>();
foreach (var node in _tree.All.Nodes)
{
if (node.Data.Equals(selected))
{
foreach (var category in node.DirectChildren.Values)
{
newcombobox.Add(category);
}
depth = node.Depth;
}
}
if (newcombobox.Count > 0)
{
Comboboxes.Add(newcombobox);
}
RemoveFollowing(Comboboxes, depth);
}
I have an optiontransferselect in a form but i dont know how to get the selected items in the rightlist back in my action.
I need to get a list with all the visited countries' ids. i tried in my action List (Integer) countriesVisitedId; but it returns nullPointerException. then i tried Integer id but it returns null.
this is what i have:
s:optiontransferselect
label="Select visited countries"
name="countriesNotVisitedId"
leftTitle="Not visited countries"
rightTitle="Visited Countries"
list="%{countriesNotVisited}"
listKey="id"
listValue="name"
headerKey="countryNotVisitedId"
headerValue="--- Please Select ---"
doubleName="countriesVisitedId"
doubleList="%{countriesVisited}"
doubleHeaderKey="countryVisitedId"
doubleHeaderValue="--- Please Select ---"
doubleListKey="id"
doubleListValue="name" />
how can I get the list with the Integers ids of the visited countries in my action?
I was banging my head on the wall wondering what I was doing wrong. It is pretty simple
doubleName="fields" is the tag field that is returned
public void setFields(String fields) { this is what needs to be in your action class.
The thing that I didn't realise is the elements need to be selected in order to be sent back. Or simple use ajax with in your header
Here's what I tried, it works fine.
Step 1: JSP to select the country from the left hand side into right hand.
<s:optiontransferselect
label="Favourite Characters"
name="leftSide"
id="left"
leftTitle="Left Title"
rightTitle="Right Title"
list="%{countriesNotVisited)"
multiple="true"
headerKey="headerKey"
doubleList="{}"
doubleId="right"
doubleName="rightSide"
doubleHeaderKey="doubleHeaderKey"
doubleMultiple="true" />
Step 2: Javascript code to auto select all data from the right hand side.
function selectall()
{
var list = document.getElementById("right");
for (var i = 0; i < list.options.length; i++)
{
alert(list.options[i].value)
list.options[i].selected = true;
}
var form = document.getElementById("right");
form.submit();
return true;
}
Step 3: call this function on submit, from the JSP side.
<s:submit id="submitid" value="Submit" action="insert" onclick="selectall()"/>
Step 4: In the action, make the getters and setters of object names of the left and right sides take strings and not string arrays.
private String leftSide;
private String rightSide;
public String getLeftSide() {
return leftSide;
}
public String getRightSide() {
return rightSide;
}
public void setRightSide(String rightSide) {
this.rightSide = rightSide;
}
public void setLeftSide(String leftSide) {
this.leftSide = leftSide;
}
Now if you try to print a value in the action, you will get values:
System.out.println("right side list " + ad.getRightSide());
In your action:
public void setCountriesVisitedId(String[] countriesVisitedId) {
this.countriesVisitedId = countriesVisitedId;
}