I create an entry using
<Entry Placeholder="Reply..."/>
It is inside a ListView > ItemTemplate > DataTemplate > ViewCell
The thing is I need a way once a user clicks the submit button in that ViewCell it gets the text for the entry in that cell. I am using Binding to set the values so I don't how to get the text.
When you handle the button's click event, assuming you are using an event handler to listen to the Clicked event, you can get the BindingContext of the button (which should also be the same BindingContext for the entire ViewCell).
Like so:
public void OnButtonClicked(object sender, EventArgs e)
{
// Assuming the List bound to the ListView contains "MyObject" objects,
// for example List<MyObject>:
var myObjectBoundToViewCell = (MyObject)((Button)sender).BindingContext;
// and now use myObjectBoundToViewCell to get the text that is bound from the Entry
}
Seeing your code I could notice why the #sme's answer doesn't fit you. You're making a very confusing and poor use of bindings and xaml, and I'm quite sure that move to MVVM is the best thing you can do now.
But, if you insist to keep the code like it is now, you can just add the Reply text bound to the Entry's text property, like that:
<Entry Placeholder="Reply..."
HorizontalOptions="FillAndExpand"
Margin="0, 0, 0, 5"
Text="{Binding Reply}"/>
So, as you are sending the entire MessageObjectobject to the tap command, you'll be able to get the text content just this way:
public void ReplyCommandClick(string id, object msg)
{
MessageObject message = (MessageObject) msg;
message.ShowReplyField = message.ShowReplyField ? false : true;
//var viewcell = (MessageObject)((Label)msg).BindingContext;
//viewcell. // There were no options for the entry
var reply = msg.Reply;
SendReply(id, msg, reply);
}
Related
I want to add buttons in a dynamic view, for example in some cases I will add 2 buttons and in certain opportunities I will add 4 buttons, but I want to generalize all this logic in a view to which I only pass parameters such as view title, number of buttons, etc ... so that then the user is deployed in the following way ...
to do so, discard certain implementations:
1.- Use a , since this generates a dead space if the list comes with few elements
2.- Use the control since this does not exist for Android-iOS
When using MVVM as an architectural pattern, it is difficult for me to pass parameters to the CodeBehind, for the previous use a MessagingCenter in which I pass a string list which I finally go through and I am adding buttons, but this is not working
you have the following view with a corresponding x: Name...
View.xaml:
<Label
Text="{Binding Titulo}"
HorizontalOptions="CenterAndExpand">
</Label>
<StackLayout
x:Name="EntriesStackLayout">
</StackLayout>
is in the ViewModel of the View where I pass the list through MessagingCenter
ViewModel.cs:
#region Constructor
public OptionsPopUpViewModel(string title, List<string> opciones)
{
Titulo = title;
opcionesMensaje = opciones;
MessagingCenter.Send((App)Xamarin.Forms.Application.Current, "Opciones", opcionesMensaje);
}
#endregion
Then in the Code behind of the View I receive in the method OnAppearing () and I create the button in the View
View.xaml.cs:
public OptionsPopUpView()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Subscribe<App, List<string>>((App)Xamarin.Forms.Application.Current, "Opciones", (s, opcionesMensaje) =>
{
foreach (var item in opcionesMensaje)
{
Button boton = new Button();
EntriesStackLayout.Children.Add(boton);
}
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<App, List<string>>(this, "Opciones");
}
Expected behavior: Adding buttons depending on the number of items in the list
Behavior obtained: when tracking the code, the first one does not execute the OnAppering () method when I run for the second time it enters the method and it executes correctly, but it is not adding the buttons
Why am I getting this behavior?
because my code is not able to add the buttons if I am receiving the list in the MessagingCenter?
What am I doing wrong?
any help for me?
I hope this question helps those who are with the same problem
regards!
I have the following label:
<Label Text="{x:Static local:FontAwesome.FACheck}" FontFamily="FontAwesome" TextColor="Green"/>
And an event on button:
correctButton.Clicked += (sender, e) =>
{
App.DB.IncrementScore();
};
What this does is everytime I click on a button my score is incremented by 1. What I wanted to do is also increment the number of Labels depending on the number of score. See the attached image below:
Anyone has any idea how I could achieve this?
There are different solutions to this question. Be sure to read through all of them before choosing one - my favorite (the simplest one) is listed all the way down...
Approach #1:
As several people have suggested, you can create some collection control (I'll come to that in a moment), define an ObservableCollection within a ViewModel, set the Page's Binding Context to an instance of that ViewModel, and add items to the collection on button click:
public class MyViewModel()
{
public ObservableCollection<int> Items { get; set; } = new ObservableCollection<int>();
}
private MyViewModel _viewModel = new MyViewModel();
public MainPage()
{
InitializeComponent();
BindingContext = _viewModel;
}
correctButton.Clicked += (sender, e) =>
{
App.DB.IncrementScore();
_viewModel.Items.Add(0);
};
The type of ObservableCollection actually does not matter, since we are using the same static ItemTemplate for all items:
<ContentPage.Resources>
<DataTemplate x:Key="ScoreItemTemplate">
<ViewCell>
<ViewCell.View>
<Label Text="{x:Static local:FontAwesome.FACheck}" FontFamily="FontAwesome" TextColor="Green"/>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ContentPage.Resources>
The main problem with this approach is that Xamarin.Forms ListView can not display its items horizontally. This means, you'd need to download one of the available HorizontalListView Nuget packages out there, or use the built-in (only in Xamarin.Forms 2.3 and above!) CarouselView:
<CarouselView ItemTemplate="{StaticResource ScoreItemTemplate}" ItemsSource="{Binding Items}"/>
Then, you'll might wish to spend some time removing all the visual effects for swiping through the carousel, and for selecting items if you choose to use a horizontal ListView...
Instead, there are two alternative solutions that involve less effort:
Approach #2:
Obviously, the simple approach would be to create the "template" label in code:
private Label CreateScoreLabel()
{
return new Label {Text = FontAwesome.FACheck, TextColor = Color.Green, FontFamily = "FontAwesome"};
}
...add a horizontal StackLayout to the page:
<StackLayout Orientation="Horizontal" x:Name="LabelStack"/>
...and add new labels the hard way:
correctButton.Clicked += (sender, e) =>
{
App.DB.IncrementScore();
LabelStack.Children.Add(CreateScoreLabel());
};
However, all this seems rather hacky for just creating a list of green check marks. This leads us to...
...approach #3:
Technically, this isn't exactly what you asked for (increment the number of Labels), but according to your screen shot it might fulfill your needs in a much simpler way.
Remove the existing label's text (as it shall display nothing at startup), and instead give it a unique name:
<Label x:Name="ScoreLabel" FontFamily="FontAwesome" TextColor="Green"/>
Now, define a simple extension method for the string type that repeats a given string for a certain number of times:
public static class StringExtensions
{
public static string Repeat(this string input, int num)
{
return String.Concat(Enumerable.Repeat(input, num));
}
}
(There are multiple ways to make this repeat function as performant as possible, I simply chose the simplest one-liner available, search StackOverflow for detailed discussions...)
You can now work with the single Label control defined in XAML, and just assign several check marks to it on button click:
correctButton.Clicked += (sender, e) =>
{
App.DB.IncrementScore();
ScoreLabel.Text = FontAwesome.FACheck.Repeat(App.DB.Score); // replace parameter by whatever method allows you to access the current score number
};
Of course, this approach can also be adapted to follow the MMVM way, by simply using a public bindable string property instead of setting the label's Text attribute directly, but for this I'd advise you to take a look at a beginner's MVVM tutorial.
Simply insert new label code in button click event which will add new children in your score stack.
cs page Code:-
button.Clicked += (sender, e) =>
{
App.DB.IncrementScore();
lblStack.Children.Add(new Label {Text = FontAwesome.FACheck, TextColor = Color.Green, FontFamily = "FontAwesome"});
};
xamal code
<StackLayout Orientation="Horizontal" x:Name="lblStack"/>
If the score has a maximum just add the max number of labels and bind their visibility.
Or you can make a listview of labels and bind the source to a collection that you increment when required.
Note: I'm not sure if this answer is correct, but it's too long for a comment. Just test it and if it doesn't work I will delete this answer
You can add controls programmatically. Keep in mind that you need to add a Label on your MainThread, because it's a change to the UI. Add this to your Clicked event:
correctButton.Clicked += (sender, e) =>
{
App.DB.IncrementScore();
Device.BeginInvokeOnMainThread(() => // On MainThread because it's a change in your UI
{
Label label = new Label();
label.Text = "{x:Static local:FontAwesome.FACheck}"; // Not sure if this is right syntax...
label.FontFamily = "FontAwesome";
label.TextColor = Color.Green;
stackPanel.Children.Add(label);
});
};
In case you have a Grid, you can set Grid.Row and Grid.Column with the SetValue method of a Control:
label.SetValue(Grid.RowProperty, 1);
label.SetValue(Grid.RowProperty, 2);
I am making a Universal Windows App, which includes inserting text into a textbox. I want my app to suggest text from a file to insert to the textbox. But I could not find that property. I have added the textbox in the MainPage.xaml through XAML tags. I believe there is a property for this operation in WPF API. I am just not sure if I can do this in UWP.
I recommend using the AutoSuggestBox control for UWP. The auto-suggest results list populates automatically once the user starts to enter text. The results list can appear above or below the text entry box.
<AutoSuggestBox PlaceholderText="Search" QueryIcon="Find" Width="200"
TextChanged="AutoSuggestBox_TextChanged"
QuerySubmitted="AutoSuggestBox_QuerySubmitted"
SuggestionChosen="AutoSuggestBox_SuggestionChosen"/>
private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
// Only get results when it was a user typing,
// otherwise assume the value got filled in by TextMemberPath
// or the handler for SuggestionChosen.
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
//Set the ItemsSource to be your filtered dataset
//sender.ItemsSource = dataset;
}
}
private void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
{
// Set sender.Text. You can use args.SelectedItem to build your text string.
}
private void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (args.ChosenSuggestion != null)
{
// User selected an item from the suggestion list, take an action on it here.
}
else
{
// Use args.QueryText to determine what to do.
}
}
Here is the link to the GitHub repo for a complete UI basics sample.
Hope this helps.
This may not apply for UAP but with WPF there's a trick that allows a "dropdown suggestion list". You can replace text box with a combobox and populate it's items when user types. This can be achieved by doing bindings like so:
Text={ Binding Path=meCurrentValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }
ItemsSource={Binding Path=meFilteredListOfSuggestions, Mode=TwoWay }
Then within your viewmodel you can simply do:
public string meCurrentValue
{
get { return _mecurrentvalue; }
set {
_mecurrentvalue = value;
updateSuggestionsList();
NotifyPropertyChanged("meCurrentValue");
NotifyPropertyChanged("meFilteredListOfSuggestions"); // notify that the list was updated
ComboBox.Open(); // use to open the combobox list
}
public List<string> meFilteredListOfSuggestions
{
get{return SuggestionsList.Select( e => e.text.StartsWith(_mecurrentvalue));}
}
EDIT:
Remember to set the editable value of the combobox to TRUE, this way it will act like a normal textbox.
In a WPF application, I am creating a setting window to customize keyboard shortcuts.
In the textboxes, I handle the KeyDown event and convert the Key event to a human readable form (and also the form in which I want to have my data).
The text box is declared like this
<TextBox Text="{Binding ShortCutText, Mode=TwoWay}"/>
and in the event handler, I tried using both
(sender as TextBox).Text = "...";
and
(sender as TextBox).Clear();
(sender as TextBox).AppendText("...");
In both of these cases, the binding back to the viewmodel does not work, the viewmodel still contains the old data and does not get updated.
Binding in the other direction (from viewmodel to the textbox) works fine.
Is there a way I can edit the TextBox.Text from code without using the binding?
Or is there an error somewhere else in my process?
var box = sender as TextBox;
// Change your box text..
box.GetBindingExpression(TextBox.TextProperty).UpdateSource();
This should force your binding to update.
Don't change the Text property - change what you are binding to.
This did the trick:
private static void SetText(TextBox textBox, string text)
{
textBox.Clear();
textBox.AppendText(text);
textBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
If your binding is destroyed by setting a new value (which is strange, for a two way binding the binding should stay intact), then use ((TextBox)sender).SetCurrentValue(TextBox.TextProperty, newValue) to leave the binding intact.
You don`t need to modify value of TextBox at all! In code you need only to modify your related value (ShortCutText) Also you can set IsReadOnly="True" property of your TextBox.
<TextBox Text="{Binding Path=ShortCutText,Mode=OneWay}"
KeyDown="TextBox_KeyDown" IsReadOnly="True"/>
You should realize INotifyPropertyChanged interface in your class as described in MSDN:
http://msdn.microsoft.com/library/system.componentmodel.inotifypropertychanged.aspx
Modify setter of your ShortCutText property (to which your TextBox is bound to):
class MyClass:INotifyPropertyChanged
{
string shortCutText="Alt+A";
public string ShortCutText
{
get { return shortCutText; }
set
{
shortCutText=value;
NotifyPropertyChanged("ShortCutText");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void NotifyPropertyChanged( string props )
{
if( PropertyChanged != null )
PropertyChanged( this , new PropertyChangedEventArgs( prop ) );
}
}
WPF will subscribe to the PropertyChanged event automatically.
Now use KeyDown event of TextBox, for example, like this:
private void TextBox_KeyDown( object sender , KeyEventArgs e )
{
ShortCutText =
( e.KeyboardDevice.IsKeyDown( Key.LeftCtrl )? "Ctrl+ " : "" )
+ e.Key.ToString( );
}
I have similar case.
When I clear the textbox this losing the Binding.
I wore: textbox1.Text = String.empty
I change for this: textbox1.Clear()
And this is point for my solution
If you are using MVVM you should not change the Text property of the TextBox from code , change the value in the view model and the pattern will do its job synchronizing the view.
You can configure it in the xaml itself:
<TextBox Text="{Binding ShortCutText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
UpdateSourceTrigger=PropertyChanged
I have a UserControl say Stock and it has a Button called Display
<Button Command="{Binding DisplayCommand}" CommandParameter="StockGroups">Display</Button>
Now when i Click this button it should add an another UserControl named Display to the Canvas which is in HomeWindow and should pass the CommandParameter to the Display userControl.
private DelegateCommand<string> _displayCommand;
public virtual void DisplayExecuted(string param){}
public ICommand DisplayCommand
{
get
{
if (_displayCommand == null)
_displayCommand = new DelegateCommand<string>(new Action<string>(DisplayExecuted));
return _displayCommand;
}
}
An alternative method which is more MVVM-ish would be to have a boolean property named ShouldDisplayControl, which is then bound to the control's Visibility property (using the [BooleanToVisibilityConverter]) 1), while passing the CommandParameter as a second property, maybe ControlParameter, which the control is also bound to.
This is not an operation that should involve the ViewModel, since it does not manipulate any model data.
Instead of a ViewModel command, consider merely handling the button's OnClick in the code-behind of the xaml.
In your HomeWindow.xaml.cs file:
protected override void Display_OnClick(object sender, EventArgs e) {
var buttonName = ((Button)sender).Name; // gets the name of the button that triggered this event
var displayControl = new DisplayControl(); // your user control
displayControl.param = buttonName; // set the desired property on your display control to the name of the button that was clicked
((Canvas)Content).Children.Add(displayControl); // 'Content' is your Canvas element
}
And in your HomeWindow.xaml file:
<Button x:Name="StockGroups" Click="Display_OnClick" Text="Display" />
That should get you what you want, without needing to create and invoke a command in the viewmodel. The name of the clicked button will be set to the specified property in your userControl, and an instance of the control will be created inside the Canvas.