I have some issue updating my online db connected listview page in 2 cases
Adding an item to the list and returning to it with PopAsync
Updating the date with a DatePicker and show data corresponding to
this date
I use a ViewModel to retrieve data from my online db, fill an observablecollection binded to the view. When i open the page with a PushAsync it loads and shows well.
If i do MainPage -> View -> PushAsync to add item page -> PopToRootAsync (main page) -> PushAsync View it shows fine too.
But i need to go on my View right after adding item with PopAsync.
But when i try to have it updated even with OnAppearing it doesn't work. The OnAppearing is triggered but the listview is not updated when i call the ViewModel.
I think the problem is the listview not updated after ClientOnGetSuccesCompleted, when i open the View with a PushAsync the listview is updated while the ObservableCollection is filled.
With OnApprearing it also fill ObservableCollection but no displayed updates.
Tried MessagingCenter but couldn't manage to make it work neither..
Thanks
The ViewModel:
public SuccesViewModel()
{
FillSuccess();
}
public void FillSuccess()
{
SuccesList = new ObservableCollection<Succes>();
var date = App.Date;
BasicHttpBinding binding = CreateBasicHttp();
this.client1 = new BienEtreServiceClient(binding, EndPoint);
this.instance = ((IBienEtreService)client1.InnerChannel);
client1.GetSuccesCompleted += ClientOnGetSuccesCompleted;
client1.GetSuccesAsync(App.UserID, date);
}
private void ClientOnGetSuccesCompleted(object sender, GetSuccesCompletedEventArgs e)
{
SuccesList.Clear();
foreach (Succes item in e.Result)
{
if (item.Date.ToString("yyyy-MM-dd") == App.Date.ToString("yyyy-MM-dd"))
{
SuccesList.Add(item);
}
}
}
XAML:
<ListView x:Name="lv_Succes" ItemsSource="{Binding SuccesList}" HasUnevenRows="True" ItemTapped="Tapped_Succes">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical"
Margin="20,8"
Padding="5"
BackgroundColor="#fcf3a8"
MinimumHeightRequest="40"
Opacity="0.7">
<Label x:Name="succes_txt"
Text="{Binding Text}"
FontAttributes="Bold"
TextColor="Black"
FontSize="Medium"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The code behind the View:
SuccesViewModel SuccesViewModel = new SuccesViewModel();
public Online_Succes()
{
InitializeComponent();
BindingContext = new SuccesViewModel();
}
protected override void OnAppearing()
{
SuccesViewModel.FillSuccess();
base.OnAppearing();
}
The method in OnAppearing is executed, the ObservableCollection filled well, but it doesn't refresh the ListView.
I notice one possible point of failure on your view model: you are creating a new instance to SuccesList on every update call. The binding was made at the first instance you give when setting the binding context of your view, those new instances created on each call of FillSuccess are not binded.
So change your view model to this:
public SuccesViewModel()
{
// Initializing viewModel
SuccesList = new ObservableCollection<Succes>();
BasicHttpBinding binding = CreateBasicHttp();
this.client1 = new BienEtreServiceClient(binding, EndPoint);
this.instance = ((IBienEtreService)client1.InnerChannel);
client1.GetSuccesCompleted += ClientOnGetSuccesCompleted;
// Updating data
FillSuccess();
}
public void FillSuccess()
{
client1.GetSuccesAsync(App.UserID, App.Date);
}
private void ClientOnGetSuccesCompleted(object sender, GetSuccesCompletedEventArgs e)
{
SuccesList.Clear();
foreach (Succes item in e.Result)
if (item.Date.ToString("yyyy-MM-dd") == App.Date.ToString("yyyy-MM-dd"))
SuccesList.Add(item);
}
This should work for you.
I hope it helps.
Related
I have this strange problem, where the binding seems completely ignored.
my xaml
<Button IsEnabled="{Binding ButtonEnabled}" x:Name="ButtonOK" BackgroundColor="Green" TextColor="White" Text="OK"/>
my C#
private bool _buttonEnabled = false;
public bool ButtonEnabled
{
get
{
// breakpoint 1, which never hits with value = false
return _buttonEnabled;
}
set
{
// breakpoint 2, which hits
_buttonEnabled = value;
OnPropertyChanged(nameof(ButtonEnabled));
}
}
private void ChassisEntry_TextChanged(object sender, TextChangedEventArgs e)
{
ButtonEnabled = ChassisEntry.Text != "";
}
private void PageScan_Appearing(object sender, EventArgs e)
{
ChassisEntry.Text = "";
}
I expect that when this page opens that ButtonOK is disabled, but it is not.
When I set breakpoints then breakpoint 1 (in the getter) never hits, its like if the xaml IsEnabled="{Binding ButtonEnabled}" is ignored.
The breakpoint 2 does hits, with value = false
What am I missing here ?
I googled this problem and found many similar questions, but all solutions given do not help with my problem.
Button IsEnabled binding not working properly
How to disable a button until all entries are filled?
Disable/Enable save button based on the mandatory field being null or empty using Behaviors
and many more
I am guessing you are using the xaml.cs page for holding your Bindings and hence if you are doing that there are two ways to do this
Set the BindingContext to the current class in the constructor before or right after InitializeComponent
BindingContext= this;
Or In your XAML
<ContentPage
....
x:Name="currentPage">
And in your button
<Button IsEnabled="{Binding ButtonEnabled, Source={x:Reference currentPage}}"
I would go with this: Set my Button in my XAML disabled.
<Button IsEnabled="False" x:Name="ButtonOK" BackgroundColor="Green" TextColor="White" Text="OK"/>
Then on my Entry control i would add the property TextChanged.
<Entry x:Name="ChassisEntry"
PlaceholderColor="DarkGray"
TextChanged="ChassisEntryChanged">
On xaml.cs file:
private void ChassisEntryChanged(object sender, TextChangedEventArgs e)
{
if (e.NewTextValue.Text != "")
{
ButtonOK.IsEnabled = true;
}
else
{
ButtonOK.IsEnabled = false;
}
}
I want to show a label when i click on my item in my listview.
The real problem i don't know how to link between my viewmodel and my views
I want modify my label in viewmodel but I don't know if its possible currently.
My xaml :
<StackLayout>
<Label x:Name="labelperso"
Text="{Binding newProduct}"
IsVisible="{Binding Addproduct}"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
BackgroundColor="#000000"
FontSize="20"
Opacity="0"/>
<ListView ItemsSource="{Binding Products}" CachingStrategy="RecycleElement" RowHeight="50" >
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding CodeReferenceLibelle}" TextColor="Black"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Behaviors>
<b:EventToCommandBehavior EventName="ItemSelected" Command="{Binding
SelectCommand}" Converter="{StaticResource SelectedItemConverter}"/>
</ListView.Behaviors>
my viewmodel :
#region labelperso property
private string _newProduct;
public string newProduct
{
get { return _newProduct; }
set { SetProperty(ref _newProduct, value); }
}
#endregion
#region Addproduct property
private bool _Addproduct;
public bool Addproduct
{
get { return _Addproduct; }
set { SetProperty(ref _Addproduct, value); }
}
#endregion
when I click on my item :
async Task Select()
{
newProduct = "Produit ajouté !";
basketManager.AddProductSkuAsync(sku);
newProduct = "";
await Task.Run(() => ShowText());
}
//I have tried this but I can't use my label in my view
async Task ShowText()
{
await labelperso.FadeTo(1);
await Task.Delay(1000);
await labelperso.FadeTo(0);
}
Why are you want to take the label "labelperso" in VM ? you can use it in xaml.cs instead.
You just need to add the event ItemSelected like this:
<ListView ItemsSource="{Binding Products}" ItemSelected="OnSelection">
In xaml.cs
void OnSelection(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null)
{
return;
}
//suppose the binding Object is Product
Product product = (Product)e.SelectedItem;
//labelperso.Text = "name = " + product.Name;
labelperso.FadeTo(1);
Task.Delay(1000);
labelperso.FadeTo(0);
}
Normally, VM are unrelated to Xaml, and we should not get labels from VM.
And we don't recommend it.But if you must, you can pass the Label in from the xaml.cs file like this:
You can define a variable in yourpage.xaml.cs:
public Label pageLabel;
and initial like this:
pageLabel = labelperso;
BindingContext = new YourViewmodel(this);
And in YourViewmodel.cs:
public Label ss;
public YourViewmodel(ContentPage parentPage)
{// here HomePage is your contentPage name of the page`
ss = ((HomePage)parentPage).pageLabel;//after this you can use it
}
You need to add a SelectedProduct property to your VM.
private string _SelectedProduct;
public string SelectedProduct
{
get { return _SelectedProduct; }
set { SetProperty(ref _SelectedProduct, value); }
}
You can then bind your ListView's SelectedItem to it
<ListView ItemsSource="{Binding Products}"
SelectedItem="{Binding SelectedProduct}"
CachingStrategy="RecycleElement"
RowHeight="50" >
You can then control the visibility of your label by binding to SelectedProduct via a "nullToVisibility" converter, or by using triggers etc.
You should try to use MVVM pattern rather than hacking with code behind.
Using MVVM you can add a Visible property to your viewmodel and bind the IsVisible property of the label to it.
Code will be much easy to read and maintain.
Hello I want to create notify icon to my task-bar and when I click that icon one popup window open and that popup showing me which tasks are I have to complete today and also want to show today's appointment list.
Doubts
Suppose I get 10 task from database for today's date then All task should be display with scroll bar.
How to bind data with WPF control([textBlock])?
How to create [textBlock] control dynamically means Suppose I get task description from description column then it display otherwise description [textBlock] is not create.
I have refereed following link to achieve this.
http://www.codeproject.com/Articles/36468/WPF-NotifyIcon
but I really don't know how to bind data with WPF application.
Edit the FancyPopup.xaml
Add:
<ListView ItemsSource="{Binding TasksCollection, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TaskName}"/>
</DataTemplate>
<ListView.ItemTemplate/>
</ListView>
In the code behind you can set your view model like this:
public FancyPopup()
{
InitializeComponent();
this.DataContext = new PopupViewModel();
}
And then in your ViewModel:
public ObservableCollection<TaskDataModel> tasksCollection;
public ObservableCollection<TaskDataModel> TasksCollection
{
get
{
if (tasksCollection == null)
{
tasksCollection = new ObservableCollection<TaskDataModel>();
}
return tasksCollection;
}
set
{
tasksCollection = value;
this.OnPropertyChanged("tasksCollection");
}
}
Where TaskDataModel is class describing your data model.
public class TaskDataModel : INotifyPropertyChanged
{
public TaskDataModel()
{
}
private string taskName;
public string TaskName
{
get { return taskName; }
set
{
if (taskName != value)
{
taskName = value;
OnPropertyChanged("TaskName");
}
}
}
}
I am new to silverlight. I am trying to generate a list of checkboxes(with content). The idea is that the user will select some of these checkboxes and will press a button. Then we try to read the content of selected checkboxes for further processing. I don't know how many number of checkboxes will be there and therefore I can't use bindings.
This is the code snippet in the .xaml file.
<StackPanel Grid.Row="21" Grid.Column="1" Margin="5" VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ItemsControl Name="infoPairItems" ItemsSource="{Binding InfoPair}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Grid.Column="0" Name="infoPairSelectBox" IsEnabled="True" IsThreeState="False"
Margin="0,5" FontSize="12" IsChecked="bool"
Content="{Binding Converter={StaticResource infoPairToStringValueConverter}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</StackPanel>
I am trying to access these checkboxes in the .xaml.cs file like this.
foreach(var infoPairItem in infoPairItems.Items)
{
ContentPresenter container = infoPairItems.ItemContainerGenerator.ContainerFromItem(infoPairItem) as ContentPresenter;
if(container == null)
{
DebugLog.Log("container is null ");
}
DataTemplate dataTemplate = container.ContentTemplate;
CheckBox checkBox = (CheckBox)dataTemplate.LoadContent();
if (checkBox == null)
{
DebugLog.Log("checkBox is null !!!");
return;
}
if (checkBox.IsChecked.HasValue)
{
if (checkBox.IsChecked.Value)
{
DebugLog.Log("checkbox value true");
}
else
{
DebugLog.Log("checkbox value false");
}
}
}
The log 'checkbox value false' is always getting printed for all the checkboxes even when some of them are selected. I tried to use the debugger. It looks like that variable container is getting loaded with the correct value. Either the method LoadContent() is not working or I am using the wrong method.
I apologize beforehand if it is a repeat question. I tried to look into the previous questions on stackoverflow but could not find any answer. Please guide me in correct direction.
I will explain what happens and how to solve:
1.- You are getting the datatemplate not instances of the datatemplate, in case you want to manage the instances you can do by using the Loaded Event to add items to the List to create and update for instance a List.
2.- What makes all of these events a really complex code to manage is easier if you create the following:
2.1 A class for instance that has a bool and a string for the content with INotifyPropertyChanged:
public class InfoSelection : Model
{
Property bool for Selected
Property string for Info, or whatever and the converter
}
2.2 A list with the items you need of the type of that class in the DataContext
public List<InfoSelection> {get;set;}
(If you initialize just once in the constructor for instance, you do not need to implement INotiyPropertyChanged, just clear or removeitems, never reassign)
2.3 In the Xaml binding change to the following:
<CheckBox Grid.Column="0"
Name="infoPairSelectBox"
IsEnabled="True"
IsThreeState="False"
Margin="0,5"
FontSize="12"
IsChecked="{Binding Selected, Mode=TwoWay}"
Content="{Binding Info}"/>
I don't know how many number of checkboxes will be there and therefore I can't use bindings.
Incorrect.
To Visually display two levels of data generically, the use of a ItemsControl with individual DataTemplate`s for the parent items and their child items can be done.
Then to allow for the editing (your deletion operation) one needs to identify who the parent node is from the child nodes, along with getting the state of the checkbox.
That identification requires us to project the initial data into a wrapper class to facilitate binding/identification.
Let me explain.
Say our data displays a top level last name and all first names associated with the last name.
The above simulates a top level checkbox (to delete all) and children checkbox (to delete an individual item) for the following data class retrieved from the database:
public class VectorStrings
{
public int Id { get; set; }
public string LastName { get; set; }
public List<string> FirstNames { get; set; }
}
With simulated data loaded as such:
LastNames = new List<VectorStrings>()
{
new VectorStrings() { Id=9, LastName="Smith", FirstNames = new List<string>() { "Bob", "Victoria" } },
new VectorStrings() { Id=12, LastName="Jones", FirstNames = new List<string>() { "Peter", "Paul", "Mary" } },
};
Now for display I can generically display those items to the above data, but because we need to operate on the child data, we need to project that information into a holding wrapper class.
public class VectorCheckbox
{
public int Id { get; set; }
public int ParentId { get; set; }
public string DisplayName { get; set; }
public List<VectorCheckbox> Children { get; set; }
public object Tag { get; set; } // Same concept as a visual control property 'Tag'
}
so our code to project the original data looks like this:
CheckBoxData =
LastNames.Select(ln => new VectorCheckbox()
{
DisplayName = ln.LastName,
Id = ln.Id,
Tag = ln,
Children = ln.FirstNames.Select((fn, index) => new VectorCheckbox()
{
Id = index,
ParentId = ln.Id,
DisplayName = fn,
Tag = ln.Id // Hold the parent Id for identification
}).ToList()
})
.ToList();
Sweet! Now we just need nested ItemControl classes to display our data:
<ItemsControl ItemsSource="{Binding CheckBoxData}">
<ItemsControl.Resources>
<system:String x:Key="Format">Delete All for ID: '{0}' </system:String>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Margin="10" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="6,0,0,0">
<CheckBox Tag="{Binding Id}" Margin="0,0,0,10" Click="DeleteAll_Click">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id, StringFormat={StaticResource Format}}"/>
<TextBlock Text="{Binding DisplayName}" Margin="4,0,6,0"/>
<ItemsControl ItemsSource="{Binding Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Tag="{Binding Tag}"
Content="{Binding DisplayName}"
Click="DeleteIndividual_Click"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</CheckBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
Now in code behind we subscribe to the click events. I use a message box to show that I have identified the right items. For example if one clicks on a delete all check box, it identifies the children and the state of the checkbox, and if I click on a child it identifies its parent and itself.
Parent Click
private void DeleteAll_Click(object sender, RoutedEventArgs e)
{
var cb = sender as CheckBox;
if (cb != null)
{
var id = (int)cb.Tag;
var nameInstance = ViewModel.LastNames.FirstOrDefault(nm => nm.Id == id);
if (nameInstance != null)
MessageBox.Show(string.Format("Delete all for {0} of names {1} (Status {2})",
nameInstance.LastName,
string.Join(",", nameInstance.FirstNames),
((cb.IsChecked ?? false) ? "Checked" : "UnChecked")
));
}
}
Child Click
private void DeleteIndividual_Click(object sender, RoutedEventArgs e)
{
var cb = sender as CheckBox;
if (cb != null)
{
var parentId = (int)cb.Tag; // Parent Id
var nameInstance = ViewModel.LastNames.FirstOrDefault(nm => nm.Id == parentId);
if (nameInstance != null)
MessageBox.Show(string.Format("Delete individual for {0} of name {1} (Status {2})",
nameInstance.LastName,
cb.Content,
((cb.IsChecked ?? false) ? "Checked" : "UnChecked")
));
}
}
So from that I have identified the checkbox state along with the target original items. This code simulates ultimately what you want to do. I leave the actual plumbing of the observable collection remove items up to you. But this gets the idea across.
I recommend that you experiment in WPF then take it to Silverlight, for the concepts are the same, but its easier/faster to test out in WPF.
I've implemented a Windows 8 XAML VisibilitySwitchControl that displays the first child on certain condition; otherwise the other controls are shown. The code is as follows
[ContentProperty(Name = "Items")]
public class VisibilitySwitchControl : ItemsControl
{
public VisibilitySwitchControl()
{
DefaultStyleKey = typeof(VisibilitySwitchControl);
if (Items != null)
Items.VectorChanged += OnItemsChanged;
}
public bool ShowFirst
{
get { return (bool)GetValue(ShowFirstProperty); }
set { SetValue(ShowFirstProperty, value); }
}
public static readonly DependencyProperty ShowFirstProperty =
DependencyProperty.Register("ShowFirst", typeof(bool), typeof(VisibilitySwitchControl), new PropertyMetadata(true, OnShowFirstChanged));
public object VisibleContent
{
get { return GetValue(VisibleContentProperty); }
private set { SetValue(VisibleContentProperty, value); }
}
public static readonly DependencyProperty VisibleContentProperty =
DependencyProperty.Register("VisibleContent", typeof(object), typeof(VisibilitySwitchControl), new PropertyMetadata(null));
private static void OnShowFirstChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
var visibilityItemsControl = d as VisibilitySwitchControl;
if (visibilityItemsControl != null)
{
visibilityItemsControl.Evaluate();
}
}
void OnItemsChanged(IObservableVector<object> sender, IVectorChangedEventArgs evt)
{
Evaluate();
}
void Evaluate()
{
if (Items != null && Items.Any())
{
var controls = Items.OfType<FrameworkElement>().ToList();
for (var i = 0; i < controls.Count; i++)
{
if (i == 0)
{
VisibleContent = controls[i];
controls[i].Visibility = ShowFirst ? Visibility.Visible : Visibility.Collapsed;
}
else
{
controls[i].Visibility = !ShowFirst ? Visibility.Visible : Visibility.Collapsed;
}
}
}
else
{
VisibleContent = null;
}
}
}
However, if I place two ListView controls inside my VisibilitySwitchControl the ListView can grow in way that it is larger than the page and no scrollbars are shown. It doesn't stop a the parent containers bounds.
<custom:VisibilitySwitchControl ShowFirst="{Binding Path=IsFirstLevelNav}">
<ListView x:Name="FirstListView"
VerticalAlignment="Stretch"
ItemsSource="{Binding ...}"
SelectedItem="{Binding ..., Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
/>
<ListView x:Name="SecondListView"
VerticalAlignment="Stretch"
ItemsSource="{Binding ...}"
SelectedItem="{Binding ..., Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
/>
</custom:VisibilitySwitchControl>
How can I enforce a VerticalAlignment="Stretch" behavior of the children? If I remove my control and place only one the lists directly in the code, everything works as expected.
Thanks for suggestions.
you want to stretch the Height of the listview try binding it to the actual height of the parent
Heres the code part you need to include
Height="{Binding ActualHeight, ElementName=parentContainer}"
where parentContainer is the name of the custom:VisibilitySwitchControl you are using . this will bind the height to the parent container's display height. Try and let me know
If what you want is that you scroll one ListView and then when you reach the end it show the second ListView then you just need to add a ScrollViewer around the ItemPresenter inside the style of VisibilitySwitchControl and disable the ListView ScrollViewer. Just note that it mean that you will lost the virtualisation inside the ListView.
If what you want is each ListView taking half the screen than the easiest is probably to just set a Fix height for each items depending on Window.Current.Bounds.Height and register for Window.Current.SizeChanged to update it when the windows heigh changed (make sure to unregister it in unloaded to prevent memory leak).
An alternative which I think would be more complicated, would be to change the ItemsPanel of VisibilitySwitchControl to something else (by default it is a Stack panel so it will grow larger than the screen) like for example to a Grid in which you set as many row with star heigh as items you have (and then you will need to set the row of each item) or by creating a custom Panel.