I installed nudget autocomplete toolkit but unfortunately I found it wierd that this control doesn't have enough property to serve as autocomplete. It has ItemsSource but it doesn't show the list of items filtered when you type something. I am also looking for something like textChanged so that I can invoke my service and get the result again and bind the itemsource.
Here's my implementation used in Group Contacts:
XAML:
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:behaviors="using:MyNamespace.Behaviors"
.
.
<TextBox x:Name="Searchbox" PlaceholderText="contact's name" Width="250"
IsTextPredictionEnabled="False"
IsSpellCheckEnabled="False"
VerticalAlignment="Center">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="KeyUp">
<behaviors:FilterContactAction />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</TextBox>
Code:
public class FilterContactAction : DependencyObject, IAction
{
string _previousResult = null;
public object Execute(object sender, object parameter)
{
var textbox = sender as TextBox;
var keyEventArgs = parameter as KeyRoutedEventArgs;
var noChanges = textbox.Text == _previousResult;
var deletionOccurred = keyEventArgs.Key == VirtualKey.Back ||
keyEventArgs.Key == VirtualKey.Delete;
if (noChanges || deletionOccurred)
{
return null;
}
var viewModel = ResourceLocator.Instance[typeof(HomeViewModel)] as HomeViewModel;
viewModel.CanSearch = FindMatch(textbox, viewModel.Contacts);
return null;
}
private bool FindMatch(TextBox textbox, ObservableCollection<Contact> contacts)
{
foreach (var contact in contacts)
{
var suggestionDisplayed = DisplaySuggestion(textbox, contact);
if (suggestionDisplayed)
{
return true;
}
}
return false;
}
private bool DisplaySuggestion(TextBox textbox, Windows.ApplicationModel.Contacts.Contact contact)
{
var characterCount = textbox.Text.Count();
var suggestionDisplayed = false;
var isMatch = contact.DisplayName.ToUpper().StartsWith(textbox.Text.ToUpper());
if (isMatch)
{
textbox.Text = contact.DisplayName;
textbox.SelectionStart = characterCount;
textbox.SelectionLength = textbox.Text.Length - textbox.SelectionStart;
_previousResult = textbox.Text;
suggestionDisplayed = true;
}
return suggestionDisplayed;
}
}
TextBoxExt control from Syncfusion WinRT Studio has enough features to work as an AutoComplete. It has more than 15 suggestion modes including custom filter option. Hope this helps.
http://www.syncfusion.com/products/winrt/controls
Not sure what you used. Can't tell you why it doesn't work either. however last year I wanted AutoCompleteTextBox and ended up writing it myself.
you can find it here.
https://github.com/hermitdave/HermitDaveWinRTControls
Related
I have a Weather VM, that get my city and country code perfectly, but I am trying to bind the rest of my data to XAML.
All my data is store in the array, and for some reason, when I loop, the variable i, does not increment and the app crash.
I also have the problem of the icon, I have a list, that is the itemsource of my Listview, but the Icon is on another array
I tried to loop, and assign each variable, and the loop always crash
var splitedData = cityData.Split(",");
var city = splitedData[0];
var conutryCode = splitedData[1];
var data = await WeatherAPI.GetWeatherDataAsync(city, conutryCode);
if (data != null) {
for (int i = 0; i < data.list.Count; i++) {
rootObject.list[i].dt_txt = data.list[i].dt_txt;
rootObject.list[i].main.temp_max = data.list[i].main.temp_max;
rootObject.list[i].main.temp_min = data.list[i].main.temp_min;
rootObject.list[i].weather[i].icon
}
}
}
xaml listView
Margin="20" ItemsSource="{Binding rootObject.list}">
<ListView.ItemTemplate>
<DataTemplate>
<RelativePanel>
<TextBlock x:Name="dateTB"
Text="{Binding dt_txt}"
RelativePanel.RightOf="iconTB"
RelativePanel.AlignTopWith="iconTB" />
<TextBlock x:Name="highTB"
Text="{Binding main.temp_max}"
RelativePanel.RightOf="iconTB"
RelativePanel.Below="dateTB"
FontSize="10" />
<TextBlock x:Name="lowTB"
RelativePanel.RightOf="highTB"
RelativePanel.Below="dateTB"
FontSize="10"
Text="{Binding main.temp_min}"
Margin="10,0,0,0" />
<Image x:Name="iconTB"
Source="{}"
Height="30"
Width="30"
/>
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I expect the Dates to appear, the High to appear and the low to appear.
Also I have don't have a clue of how to bind the icon, becouse the icon is in Weather, and not the RootObject list, that is the itemsource of my list
I tried your code in my side and find something might be the reason. The problem might be that you didn't create instances for main object or weather list or day list. So you are assigning values to null and then it gives error.
I changed it a little bit, you can try in your side.(I still can't use MapLocator.GetCityData(); so I haven't tried this with real data.)
public class RootObject : Notify
{
private List<Day> _list;
public List<Day> list
{
get { return _list; }
set
{
if (value != _list)
{
_list = value;
onPropertyChanged("list");
}
}
}
public RootObject()
{
//I added five objects for test. In real scenario, the count depends on the data number you get
list = new List<Day>();
list.Add(new Day());
list.Add(new Day());
list.Add(new Day());
list.Add(new Day());
list.Add(new Day());
}
}
public class Day : Notify
{
private Main _main;
public Main main
{
get { return _main; }
set
{
if (value != _main)
{
_main = value;
onPropertyChanged("main");
}
}
}
private List<Weather> _weather;
public List<Weather> weather
{
get { return _weather; }
set
{
if (value != _weather)
{
_weather = value;
onPropertyChanged("weather");
}
}
}
private string _dt_txt;
public string dt_txt
{
get { return _dt_txt; }
set
{
if (value != _dt_txt)
{
_dt_txt = value;
onPropertyChanged("dt_txt");
}
}
}
public Day()
{
main = new Main();
//I added five objects for test. In real scenario, the count depends on the data number you get
weather = new List<Weather>();
weather.Add(new Weather());
weather.Add(new Weather());
weather.Add(new Weather());
weather.Add(new Weather());
weather.Add(new Weather());
}
}
For local test, I just added 5 objects. In your real scenario, you need to check the count of data from bingmap.
From my previous post, it helped be to determine how to bind to selecteditems, How to bind to autocomplete selecteditem with ObservableCollection But now I'm trying to enhance that logic.
I'm trying to have items preselected when my View is initialized. I've tried multiple options, but I can't seem to get items preselected. May I get some assistance. My current code below
Keyword Class
public class Keyword : ObservableObject
{
private string _value;
public string Value
{
get { return _value; }
set { SetProperty(ref _value, value); }
}
}
ViewModel
private ObservableCollection<object> _selectedKeywords = new ObservableCollection<object>();
private ObservableCollection<Keyword> _keywords = new ObservableCollection<Keyword>();
public TestViewModel()
{
Keywords = new ObservableCollection<Keyword>()
{
new Keyword { Value = "Apples" },
new Keyword { Value = "Bananas" },
new Keyword { Value = "Celery" }
};
SelectedKeywords = new ObservableCollection<object>(Keywords.Where(x => x.Value == "Apples"));
}
public ObservableCollection<object> SelectedKeywords
{
get { return _selectedKeywords; }
set { SetProperty(ref _selectedKeywords, value); }
}
public ObservableCollection<Keyword> Keywords
{
get { return _keywords; }
set { SetProperty(ref _keywords, value); }
}
View
<autocomplete:SfAutoComplete MultiSelectMode="Token"
HorizontalOptions="FillAndExpand"
VerticalOptions="EndAndExpand"
TokensWrapMode="Wrap"
Text="{Binding Keyword, Mode=TwoWay }"
IsSelectedItemsVisibleInDropDown="false"
Watermark="Add..."
HeightRequest="120"
SelectedItem="{Binding SelectedKeywords}"
DataSource="{Binding Keywords}">
</autocomplete:SfAutoComplete>
We have prepared sample from your code snippet and you have missed to add DisplayMemberPath property in the code snippet. Please find the sample from below location.
http://www.syncfusion.com/downloads/support/directtrac/general/ze/AutoCompleteSample-270923957.zip
Note: I work for Syncfusion.
Regards,
Dhanasekar
To make it preselected in your View Model set a value to the binding that you have binded on your View basically assign a value to SelectedKeywords
Something like:
SelectedKeywords = Keywords.FirstOrDefault();
You might need two-way binding not sure cause never used this control:
SelectedItem="{Binding SelectedKeywords, Mode=TwoWay}"
My XAML ComboBox control is stuck in an endless loop when a selection is changed in the UI. The ComboBox sets the value of the bound property. When the property has changed, it raises a property changed event. This in turn causes the databinder to update the property again. This keeps looping until I get a stack overflow exception.
<ComboBox x:Name="OriginCountryCode" Grid.ColumnSpan="2" Grid.Column="2" SelectedValue="{x:Bind Mode=TwoWay, Path=ViewModel.OriginCountryCode}" DisplayMemberPath="Value" SelectedValuePath="Key" ItemsSource="{x:Bind ViewModel.CountryCodes}" />
The control is bound to the following properties.
private static Dictionary<string, string> _countryCodes = null;
public Dictionary<string, string> CountryCodes
{
get
{
if (_countryCodes != null) return _countryCodes;
_countryCodes = new Dictionary<string, string>();
var cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
foreach (var culture in cultures)
{
var region = new RegionInfo(culture.LCID);
_countryCodes[region.TwoLetterISORegionName] = region.DisplayName;
}
return _countryCodes;
}
}
public string OriginCountryCode
{
get => _origin.CountryCode;
set
{
_origin.CountryCode = value; RaisePropertyChanged(nameof(OriginCountryCode));
}
}
This behavior is odd as all my other controls do not exhibit this behavior. The BAML generated connector code for the ComboBox is different. One is is updated when there's a focus change and the other when the SelectedValue changes.
case 15: // Views\QuotesPage.xaml line 77
this.obj15 = (global::Windows.UI.Xaml.Controls.TextBox)target;
(this.obj15).LostFocus += (global::System.Object sender, global::Windows.UI.Xaml.RoutedEventArgs e) =>
{
if (this.initialized)
{
// Update Two Way binding
this.dataRoot.ViewModel.DestinationPostalCode = this.obj15.Text;
}
};
break;
case 16: // Views\QuotesPage.xaml line 78
this.obj16 = (global::Windows.UI.Xaml.Controls.ComboBox)target;
(this.obj16).RegisterPropertyChangedCallback(global::Windows.UI.Xaml.Controls.Primitives.Selector.SelectedValueProperty,
(global::Windows.UI.Xaml.DependencyObject sender, global::Windows.UI.Xaml.DependencyProperty prop) =>
{
if (this.initialized)
{
// Update Two Way binding
this.dataRoot.ViewModel.DestinationCountryCode = (global::System.String)this.obj16.SelectedValue;
}
});
break;
Don't raise PropertyChanged when the property value hasn't changed.
public string OriginCountryCode
{
get => _origin.CountryCode;
set
{
if (_origin.CountryCode != value)
{
_origin.CountryCode = value;
RaisePropertyChanged(nameof(OriginCountryCode));
}
}
}
I have a binding of the following form in XAML,
Title="{Binding SelectedNewsItems[0].Title}"
Note that it refers to a particular element in the SelectedNewsItems which is an ObservableCollection. (I have a collection of nine buttons of various sizes, each styled, and sized differently and so a more standard ListView is not appropriate.)
When I reassign SelectedNewsItems I raise a PropertyChanged event for SelectedNewsItems, however, this does not appear to cause the bindings to update for the individual bound elements and their properties. I have tried the following,
public ObservableCollection<NewsItem> _selectedNewsItems;
public ObservableCollection<NewsItem> SelectedNewsItems
{
get
{
return this._selectedNewsItems;
}
set
{
if (this._selectedNewsItems != value)
{
this._selectedNewsItems = value;
this.NotifyPropertyChanged();
for (int i = 0; i < this._selectedNewsItems.Count; i++)
{
this.NotifyPropertyChanged(String.Format("SelectedNewsItems[{0}].Content", i));
this.NotifyPropertyChanged(String.Format("SelectedNewsItems[{0}].Title", i));
this.NotifyPropertyChanged(String.Format("SelectedNewsItems[{0}].Id", i));
this.NotifyPropertyChanged(String.Format("SelectedNewsItems[{0}].Image", i));
}
}
}
}
Hmm, I cannot exacly say where your code is wrong (as I see only part of it), but maybe you haven't set your DataContex or something else. For the purpose of research I've made simple example, which works quite fine. Take a look at it and maybe it will help you:
In Xaml:
<Button x:Name="first" VerticalAlignment="Top" Content="{Binding SelectedNewsItems[0].Name}" Grid.Row="0"/>
<Button x:Name="second" VerticalAlignment="Center" Content="{Binding SelectedNewsItems[1].Name}" Grid.Row="1"/>
In code behind (I put all the code - yeah quite a lot of, but I cannot guess what is wrong with your code):
public class NewsItem
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
public partial class MainPage : PhoneApplicationPage, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseProperty(string property = null)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
private ObservableCollection<NewsItem> _selectedNewsItems = new ObservableCollection<NewsItem>();
public ObservableCollection<NewsItem> SelectedNewsItems
{
get
{
return this._selectedNewsItems;
}
set
{
if (this._selectedNewsItems != value)
{
this._selectedNewsItems = value;
this.RaiseProperty();
for (int i = 0; i < this._selectedNewsItems.Count; i++)
{
this.RaiseProperty(String.Format("SelectedNewsItems[{0}].Name", i));
}
}
}
}
public MainPage()
{
NewsItem firstT = new NewsItem() { Name = "First" };
NewsItem secondT = new NewsItem() { Name = "Second" };
SelectedNewsItems.Add(firstT);
SelectedNewsItems.Add(secondT);
InitializeComponent();
this.DataContext = this;
first.Click += first_Click;
second.Click += second_Click;
}
private void first_Click(object sender, RoutedEventArgs e)
{
NewsItem change = new NewsItem() { Name = "Changed by First" };
SelectedNewsItems[1] = change;
}
private void second_Click(object sender, RoutedEventArgs e)
{
NewsItem change = new NewsItem() { Name = "Changed by Second" };
SelectedNewsItems[0] = change;
}
}
As I click on buttons the bindigs work, so maybe it will help you.
I'm using AutocompleteBox control which gets data from REST service. My code looks like that:
MODEL:
public class Word()
{
public int wordId {get;set}
public int wordName {get;set;}
}
XAML:
<toolkit:AutoCompleteBox x:Name="AutoDictionaryTB" Populating="AutoDictionaryTB_Populating_1" HorizontalAlignment="Left" Margin="63,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="383">
</toolkit:AutoCompleteBox>
CODE BEHIND:
private void AutoDictionaryTB_Populating_1(object sender, PopulatingEventArgs e)
{
var client = new RestClient("http://XXX.XXXX.XX");
var request = new RestRequest("/XXX.XXX.XX", Method.GET);
request.AddParameter("XX", "XX");
request.AddParameter("XX", "XX");
request.AddParameter("XXX", (sender as AutoCompleteBox).Text);
var response2 = client.ExecuteAsync<List<Word>>(request, response =>
{
if (response != null && response.Data != null)
{
AutoDictionaryTB.ItemsSource = response.Data;
AutoDictionaryTB.PopulateComplete();
}
else
{
MessageBox.Show("Unknow error occured. Check your Internet connection or try later.");
}
});
}
IN THE LINE:
AutoDictionaryTB.ItemsSource = response.Data;
I have all walues I need so service is working perfectly. Why the control is not showing these walues? It's empty although response.Data is full of words.
Try adding ValueMemberBinding="{Binding wordName}" to the AutoCompleteBox to tell the component which property of the class it should use. If it doesn't work, add an ItemTemplate.