Why picker trigger twice onselectedindexchangedevent in xamarin forms - xaml

Why picker twice time triggered first time i get the values of Selecteditem second time it seems to be null all. How to resolve this issue.
private void OnSelectedIndexChanged(object sender, EventArgs e)
{
double vatPercent;
object selectedPicker = ((Picker)sender).BindingContext;
Picker picker = ((Picker)sender);
object vatItem = picker.SelectedItem;
int index = picker.SelectedIndex;
if (vatItem != null)
{
VatRate vatRate = vatItem as VatRate;
vatPercent = vatRate.Rate;
}
else
vatPercent = 0;
}

This issue has been reported:
https://github.com/xamarin/Xamarin.Forms/issues/2075
https://github.com/xamarin/Xamarin.Forms/issues/7646
You could try the suggestions below:
Update Xamarin Forms to latest version.
Use the ObservableCollection as ItemSource.

Related

XAML forms have second picker sorted based on selection in first picker

I have 2 dropdowns (pickers) on a XAML form. The first is an ObservabelCollection of Territories. The second is an ObservableCollection of type Tracks. When the form loads my ViewModel loads both collections and each collection is bound to a picker. I want to filter and display only those tracks that are associated with the selected territory in the second picker.
FYI-the second picker is disabled until a selection is made in the first. Actually I don't require that the second picker's data source be set as the form loads, unless the solution requires it. The selection in the first picker will be the key to filter the data for the second.
I have tried to handle the filtering in the Territory picker's SelectedIndexChanged event but my ObservableCollection 'tracks' is not exposed here.
private void territoryPicker_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = (Picker)sender;
string territory = picker.SelectedItem.ToString();
Tracks _track = tracks.Where(X => X.Territory = territory);<<<==== Does not work
trackPicker.ItemsSource = _track.Track;<<<==== Does not work
trackPicker.IsEnabled = true;
}
I've also tried to not build the Tracks OC until after the Territory is selected like this:
private void territoryPicker_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = (Picker)sender;
string territory = picker.SelectedItem.ToString();
TrackViewModel vm = new TrackViewModel();
var _tracks = (Tracks)vm.Tracks; <<<<<==== This does not work
trackPicker.IsEnabled = true;
}
The ViewModel runs and the tracks are loaded via the API but when it returns here Tracks is empty.
I'm open to a reasonable solution (not 3rd party controls/packages) that will accomplish this task . Thanks
I figured it out by stumbling over the answer while researching another issue. I have been referencing my viewmodel in the {content].xaml page like this.
<ContentPage.BindingContext>
<vm:TrackViewModel />
</ContentPage.BindingContext>
When doing so the resources of that vm were not available in the code behind. But when I reference the VM in the page constructor Like this:
public partial class RequestmyTracks : ContentPage
{
TrackViewModel trackviewmodel = new TrackViewModel();
And then bind it here:
public RequestmyTracks()
{
InitializeComponent();
BindingContext = trackviewmodel;
}
The trackviewmodel is accessible in the SelectedIndexChanged event and everything else was really straight forward, Which looks like this:
private void territoryPicker_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = (Picker)sender;
string territory = picker.SelectedItem.ToString();
ObservableCollection<Tracks> dah = (ObservableCollection<Tracks>)trackviewmodel.Tracks;
List<string> trackNames = new List<string>();
foreach (var track in dah)
{
if (track.Territory == territory)
trackNames.Add(track.Track);
}
trackPicker.ItemsSource = trackNames;
if(trackNames.Count ==1)
trackPicker.SelectedIndex = 0;
trackPicker.IsEnabled = true;
}
Thanks for the help

UWP - Saving Settings does not work all the time

I've got the following code just copied from here. I want to bind a double value to a xaml slider, get this value from the localsetting every time I navigate to the SettingsPage and everytime the slidervalue gets changed by the user I want it to be saved to localsettings. Here is my code so far:
SettingsPage.xaml.cpp:
Windows::Storage::ApplicationDataContainer^ localSettings = Windows::Storage::ApplicationData::Current->LocalSettings;
SettingsPage::SettingsPage()
{
InitializeComponent();
this->viewModel = ref new SettingsViewModel();
this->DataContext = this->viewModel;
}
void SettingsPage::QSlider_ValueChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs^ e)
{
Windows::Storage::ApplicationDataCompositeValue^ composite =ref new Windows::Storage::ApplicationDataCompositeValue();
bool a = composite->Insert(SETTINGS_TAG_SLIDER_Q, dynamic_cast<PropertyValue^>(PropertyValue::CreateDouble((double)sldQ->Value)));
auto values = localSettings->Values;
bool b = values->Insert(SETTINGS_TAG_SETTINGS_PAGE, composite);
}
SettingsPage.xaml:
<Slider x:Name="sldQ" Margin="15,5,15,0" Value="{Binding SliderQValue}" ValueChanged="Slider_ValueChanged" MaxWidth="300" HorizontalContentAlignment="Left" ></Slider>
SettingsViewModel.cpp:
double SettingsViewModel::SliderQValue::get()
{
Windows::Storage::ApplicationDataContainer^ localSettings = Windows::Storage::ApplicationData::Current->LocalSettings;
ApplicationDataCompositeValue^ composite = safe_cast<ApplicationDataCompositeValue^>(localSettings->Values->Lookup(SETTINGS_TAG_SETTINGS_PAGE));
if (composite != nullptr)
{
if (composite->HasKey(SETTINGS_TAG_SLIDER_Q)) {
double value = safe_cast<IPropertyValue^>(composite->Lookup(SETTINGS_TAG_SLIDER_Q))->GetDouble();
return value;
}
}
return 99;
}
My Problem is that this works exactly once! If I navigate from other pages to SettingsPage, I get slidervalue=99. Then I set it by dragging to e.g. 50. Then I navigat back to other page. From the other page I navigate again to SettingsPage and get slidervalue=50. But doing it once again I get 99 again. So it only works for 1 page navigation-cycle but it should work even if the app is rebooted. What is the problem in my code? Am I understanding something wrong?
I actually solved the problem with the help of this. In my code above I was initializing a new 'ApplicationDateCompositeValue' each time I wanted to write/read it. So with the new method it works like it was planned to do:
OnValueChanged:
Windows::Storage::ApplicationDataContainer^ localSettings = Windows::Storage::ApplicationData::Current->LocalSettings;
auto values = localSettings->Values;
values->Insert(TAG_SLIDER, dynamic_cast<PropertyValue^>(PropertyValue::CreateDouble((double)sldQuality->Value)));
Property::get():
ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings;
auto values = localSettings->Values;
if (localSettings->Values->HasKey(TAG_SLIDER)) {
double value = safe_cast<double>(localSettings->Values->Lookup(TAG_SLIDER));
return value;
}
else
return default_value;

How to set the name of a snapshot file

I am developing a J2ME application to run on my W595s Sony Ericsson mobile phone.
My application uses the JSR 135 Mobile Media API and the JSR 234 Advanced Multimedia Supplements API.
My application displays a Form.
The camera video is displayed in a Form's item.
The Form has a command.
The application takes a snapshot when the user activates the command.
The snapshot file is saved to the Picture directory on the memory stick.
Here is the Form's commandAction event listener :
public void commandAction(Command arg0, Displayable arg1) {
m_snapshotControl.setDirectory("e:/Picture");
m_snapshotControl.setFilePrefix("AC");
m_snapshotControl.setFileSuffix(".JPG");
int[]resolutions = m_cameraControl.getSupportedStillResolutions();
int maxValue = (resolutions.length / 2) - 1;
m_cameraControl.setStillResolution(maxValue);
m_snapshotControl.start(1);
}
I ran my application 2 times.
The Picture directory did not contain any snapshot file before the first run.
I did the following actions during each run :
I activated the command
I answered yes to the following rights requesting dialogs :
Allow the application to read user data ?
Allow the application to write user data ?
Allow the application to read user data ?
Allow the application to write user data ?
Allow the application to shoot with the camera ?
Allow the application to read user data ?
Allow the application to write user data ?
The AC0000.jpg snapshot file was created after the first run.
The AC0000.jpg picture file was replaced after the second run.
I do not want my application to replace snapshots taken during past runs.
How can I set the name of a snapshot file before taking the snapshot ?
Is it possible to set the string in between the prefix and the suffix ?
Any help will be greatly appreciated
I haven't tried on this particular range of phone but how about this:
private int iSnapshotCounter = 0;
public void commandAction(Command arg0, Displayable arg1) {
m_snapshotControl.setDirectory("e:/Picture");
m_snapshotControl.setFilePrefix("AC");
m_snapshotControl.setFileSuffix( (++iSnapshotCounter) + ".JPG");
int[]resolutions = m_cameraControl.getSupportedStillResolutions();
int maxValue = (resolutions.length / 2) - 1;
m_cameraControl.setStillResolution(maxValue);
m_snapshotControl.start(1);
}
If that works, you can then decide to include the date and time (say, to the second) in the file name.
Of course, if setFileSuffix() won't allow you to specify more than a file extension, you can try to use the same trick on the prefix string.
You may also need to use JSR-75 to figure out what files already exist in the folder.
I have added a PlayerListener to the SnapshotControl's Player.
My playerUpdate method renames the created Snapshot file when the SHOOTING_STOPPED event occurs.
The new name is made up of the current date and time's parts.
The format of the new name is YYYYMMDD_HHMMSS.jpg.
Here is the playerUpdate method :
public void playerUpdate(Player arg0, String arg1, Object arg2) {
if (arg1.equalsIgnoreCase(SnapshotControl.SHOOTING_STOPPED)) {
FileConnection fconn = null;
try {
fconn = (FileConnection)Connector.open("file:///e:/Picture/" + (String)arg2);
Date now = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(now);
StringBuffer buffer = new StringBuffer();
buffer.append(calendar.get(Calendar.YEAR));
int month = calendar.get(Calendar.MONTH);
month++;
if (month < 10) {
buffer.append(0);
}
buffer.append(month);
int day = calendar.get(Calendar.DAY_OF_MONTH);
if (day < 10) {
buffer.append(0);
}
buffer.append(day);
buffer.append("_");
int hour = calendar.get(Calendar.HOUR_OF_DAY);
if (hour < 10) {
buffer.append(0);
}
buffer.append(hour);
int minute = calendar.get(Calendar.MINUTE);
if (minute < 10) {
buffer.append(0);
}
buffer.append(minute);
int second = calendar.get(Calendar.SECOND);
if (second < 10) {
buffer.append(0);
}
buffer.append(second);
buffer.append(".jpg");
fconn.rename(buffer.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
fconn.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

Get SQL data via listbox

Ok, so what I am trying to do is click one item in a listbox that i have, that listbox gets data from the sql database depending on that the user types into the textbox.
Now when I click that item in the first listbox, I need more info related to that item to show up in the 2nd list box.
When the user enters a name in the textbox, the first 10 in sql show up, now that I have that, I need to click on one of the items and get the 'task' of that client in the next listbox. Task is MATTERS in the database.
I am pretty sure that my code is correct, I get no errors but nothing shows up in the list box.
Here is my code:
private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
string item = listBox1.SelectedItem.ToString();
if (listBox1.ContainsFocus)
{
if (item == "")
{
}
else
{
var con2 = Conn.ConnString();
using (SqlConnection myConnection2 = new SqlConnection(con2))
{
string oString2 = "select CLIENTMATTERS.MATTER, CLIENTMATTERS.DESCRIPTION from CLIENTMATTERS join CLIENTCODES on CLIENTMATTERS.CLIENT = CLIENTCODES.CLIENT Where CLIENTCODES.DESCRIPTION = '#code1'";
SqlCommand oCmd = new SqlCommand(oString2, myConnection2);
oCmd.Parameters.AddWithValue("#code1", item);
myConnection2.Open();
oCmd.Connection.Open();
List<string> codelist2 = new List<string>();
using (SqlDataReader oReader2 = oCmd.ExecuteReader())
{
if (oReader2.HasRows)
{
string value = string.Empty;
while (oReader2.Read())
{
codelist2.Add(oReader2["MATTER"].ToString());
}
}
}
this.listBox2.DataSource = codelist2;
}
}
}
}
You need to use BindingList<> instead of List<>.
The List<> doesn't implement the ListChanged event, so the ListBox doesn't get notified when the datasource changes.
In your example it would look like this:
BindingList<string> codelist2 = new BindingList<string>();
For further information take a look at
BindingList on MSDN.

AutoCompleteBox and SearchText Clear

This is not a question but my answer to a problem I could not find a solution to on the internet.
I had a problem clearing the SearchText in an MVVM Silverlight application. I could clear clear the SelectedItem and Text but the SearchText was left behind. It is read only and cannot be changed by binding.
Example: AutoCompleteBox with a list of Countries. When the user wants to enter Australia they enter 'au' at this point the list appers with Austria and Australia. The user can then select Australia and move on. At the end of editing they click on a 'Save' button. At this point it is likely that you would want to clear the data forn for entering new data.
Even if you have bindings to the SelectedItem and the Text properties and you set them to 'null' and string.Empty respectively the SearchText property remains and the AutoCompleteBox will not clear but will contain 'au'.
I posted about this all over the internet but could get no answer on the control itself and so I came at it from a different angle which may help someone who ends up frustrated like me.
I am using a Silverlight Navigation template application which uses a NavigationFrame in which to load Silverlight pages. I noticed that if I navigated to another page and returned to my data form the SearchText was cleared. Any values that were bound to properties remained valid, just the SearchText had cleared on all AutoCompleteBoxes. I therefore used the PageConductor method of injecting the NavigationFrame into the ViewModel where I could call the refresh method. I got this method from John Papa's example from the Silverlight Firestarter event , I simply added a Refresh method to the IPageConductor interface so I am now able to call 'PageConductor.Refresh()' which is like reloading the page. I hope this helps someone out there.
var t = ProductCombo.ItemsSource;
ProductCombo.ItemsSource = null;
ProductCombo.Text = string.Empty;
ProductCombo.SelectedValue = null;
//ProductCombo.Text = string.Empty;
ProductCombo.ItemsSource = t;
Try this please.it worked for me
You must clear the property bindeaded to Text inside set part of SelectedItem Binded property, like this:
public string AnalisisText
{
get { return _analisisText; }
set
{
if (_analisisText == value)
{
return;
}
_analisisText = value;
RaisePropertyChanged(AnalisisTextPropertyName);
}
}
public DatosAutoCompletaPedidosDetalleViewDTO AnalisisSelect
{
get { return _analisisSelect; }
set
{
if (_analisisSelect == value)
{
return;
}
_analisisSelect = value;
if (_analisisSelect == null) AnalisisText = "";
RaisePropertyChanged(AnalisisSelectPropertyName);
}
}
So, when you set null to property SelectedItem , the other property will set to "".
The easiest way I've found is to extend the AutoCompleteBox:
public class AutoCompleteBoxClear : AutoCompleteBox
{
public AutoCompleteBoxClear()
{
DataContextChanged += (o, e) =>
{
if (SelectedItem == null)
Text = string.Empty;
};
}
}
Now use your new AutoCompleteBoxClear control in your XAML.
This clears the text only when autocompletebox datacontext changes to null (ie the user clicks add in the dataform.)
Note: I think DataContextChanged is available only in Silverlight 5, but I'd guess that anyone still using Silverlight these days would likely have upgraded by now...
var t = ProductCombo.ItemsSource;
ProductCombo.ItemsSource = null;
ProductCombo.Text = string.Empty;
ProductCombo.SelectedValue = null;
//ProductCombo.Text = string.Empty;
ProductCombo.ItemsSource = t;
Unfotunately this is code-behind and I needed an MVVM solution.
I recently had the same problem with my WPF app. I found out that the solution is not to set the object bound to SelectedItem to null, but to its default value. Took me a while to figure this out. So in your example, it would not be SelectedCountry = null, but SelectedCountry = new SelectedCountry(). In this case the SearchText is cleared also. Check my SO post regarding this matter: Autocompletebox doesn't clear keyboard strokes.
Sure SearchText property is read-only, but we can get the child component of AutoCompleteBox:
var searchText = autoCompBox.GetChildByType<TextBox>(item => item.Name == "Text");
And now we can reset SearchText via Text property of TextBox-component:
if (searchText != null) searchText.Text = string.Empty;
In C# 6.0 it is more laconically:
autoCompBox.GetChildByType<TextBox>(item => item.Name == "Text")?.Text = string.Empty;