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}"
Related
My Xamarin.Forms app displays the name of multiple items in a Listview. The name of the article is displayed by a Binding to the name field in the table that contains these articles (Panier).
I would like the user to be able to change the text size of the article name (when the screen resolution is low, the name is truncated).
To do this, I have provided a Parameters table which contains the size of the text chosen by the user.
But how do you indicate this in the xaml code?
Here's some of the xaml code:
<Label Text="{Binding Name}"
Margin="10,-4,0,0" FontSize="Medium" TextColor="Black"
FontAttributes="None"
VerticalOptions="Start"
Grid.Column="0"/>
So I would like to replace
FontSize="Medium"
With an instruction that would look like this:
FontSize="{Binding ???}"
The binding would fetch the value from the Parameters table.
But to find the name, I have in the code behind the instruction
BindingContext = new Panier();
Panier() contains the Name field, but obviously does not contain the text size.
You might need to change the BindingContext to go to the Parameters table, but in XAML code, FontSize is in the same place as Text.
To sum up: the user should be able to adjust the size of the text of the name of the articles displayed in the Listview himself.
Edit for to all those who answered me :
Excuse me, I am not an experienced developer, and I did not understand what you are offering me;
I believe I will add a NameFontSize field in the Panier table. I can therefore in my xaml code have the following code:
FontSize="{Binding NameFontSize}";
The disadvantage of this solution is this additional field which means that each record will have a data concerning the size of the text, which will be the same for all the records of the table. It doesn't make much sense to repeat the same information for all records.
If I am wrong and the solution you are proposing makes more sense, please explain it to me in more detail.
Thank you very much.
You could add MyFontSize and MySizeName peoperty inside the ViewModel, then you can use MySizeName to set NamedSize.Value for MyFontSize. And you also can set doulbe value for MyFontSize directly.
The Xaml code :
<Label Text="Hello Label!" FontSize="{Binding MyFontSize}"/>
The ViewModel :
public class ContactViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private double myFontSize;
public double MyFontSize
{
set
{
if (myFontSize != value)
{
myFontSize = value;
OnPropertyChanged("MyFontSize");
}
}
get
{
return myFontSize;
}
}
private string mySizeName;
public string MySizeName
{
set
{
if (mySizeName != value)
{
mySizeName = value;
OnPropertyChanged("MySizeName");
ConvertNameToSize();
OnPropertyChanged("MyFontSize");
}
}
get
{
return mySizeName;
}
}
// convert SizeName to FontSize
private void ConvertNameToSize()
{
if (mySizeName == "Default")
{
myFontSize = Device.GetNamedSize(NamedSize.Header, typeof(Label));
}
else if (mySizeName == "Micro")
{
myFontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label));
}
else if (mySizeName == "Small")
{
myFontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label));
}
else if (mySizeName == "Medium")
{
myFontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label));
}
else if (mySizeName == "Large")
{
myFontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label));
}
else if (mySizeName == "Body")
{
myFontSize = Device.GetNamedSize(NamedSize.Body, typeof(Label));
}
else if (mySizeName == "Header")
{
myFontSize = Device.GetNamedSize(NamedSize.Header, typeof(Label));
}
else if (mySizeName == "Title")
{
myFontSize = Device.GetNamedSize(NamedSize.Title, typeof(Label));
}
else if (mySizeName == "Subtitle")
{
myFontSize = Device.GetNamedSize(NamedSize.Subtitle, typeof(Label));
}
else if (mySizeName == "Caption")
{
myFontSize = Device.GetNamedSize(NamedSize.Caption, typeof(Label));
}else
{
myFontSize = Device.GetNamedSize(NamedSize.Header, typeof(Label));
}
}
public ContactViewModel()
{
// set value for test
mySizeName = "Large";
ConvertNameToSize();
}
}
Here adding INotifyPropertyChanged for ViewModel, if the binded value be changed on runtime. The view will update automatically.
The effect:
I am developing an application in Xamarin Forms using MVVM where I have this JSON:
[{
type: 'text',
title: 'Name'
value: 'John',
width: 50
},
{
type: 'radio'
Source: ['Male', 'Female']
value: 'Male',
width: 100
},
{
type: 'checkbox'
title: 'Married'
value: false,
width: 100
}]
Based on this JSON, I want to load controls (TextBox, RadioButton, and CheckBox in this example). I tried to find a solution but I couldn't get succeed.
This is a dynamic JSON retrieved from the database. It could be with different controls also.
Is there a way to implement this?
Hi #confusedDeveloper,
Jason's answer works well while not considering MVVM pattern. However with MVVM it needs a little more code.
Dynamically changing of a view's content can be achieved in MVVM pattern by simply creating a view component deriving from ContentView with a BindableProperty and handling the changes to the bindable property.
Here I have created a View component ChangingView
In Xaml of the View Component assign a content view to the ComponentView
<ContentView.Content>
<ContentView Grid.Row="1" x:Name="mainView">
<ContentView.Content>
<Label BackgroundColor="Gray" Text="Placeholder"/>
</ContentView.Content>
</ContentView>
</ContentView.Content>
In the Xaml.cs of the ComponentView create BindableProperty and handle its changes.
// Fetch the required parameter from JSON and bind it to this property
public string ViewType
{
get { return (string)GetValue(ViewTypeProperty); }
set
{
SetValue(ViewTypeProperty, value);
}
}
//Notice the OnViewChanged method subscription
public static readonly BindableProperty ViewTypeProperty = BindableProperty.Create("ViewType", typeof(string), typeof(ChangingView), "placeholder", BindingMode.Default, null, OnViewChanged);
This static method is call a method similar to code in #Jason's answer
private static void OnViewChanged(BindableObject bindable, object oldvalue, object newValue)
{
var viewControl = (bindable as ChangingView);
if (viewControl != null)
{
viewControl.ChangeView((string)newValue);
}
}
private void ChangeView(string viewType)
{
if(viewType == "Button")
{
this.mainView.Content = new Button()
{
BackgroundColor= Color.Red,
Text = "Button"
};
}
else if(viewType == "Label")
{
this.mainView.Content = new Label()
{
BackgroundColor= Color.Green,
Text = "Label"
};
}
}
Finally use the component in Xaml of the Page where you require this ChangingView, The DisplayControl is the string derived from the required JSON parameter (Done in ViewModel, I leave this up to you).
<component:ChangingView ViewType="{Binding DisplayControl}"/>
Hope it fits your scenario. Comment if further information is required.
Thanks,
there are lots of ways to tackle this - one approach would be to do something like this
foreach(var c in json)
{
switch(c.type) {
case "text":
var c = new Entry() { ... };
myLayout.Children.Add(c);
break;
case "checkbox":
var c = new Checkbox() { ... };
myLayout.Children.Add(c);
break;
case ...
}
}
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 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