How to display a label with click on listview - xaml

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.

Related

Listview refreshing on OnAppearing with webservice

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.

How to dynamically bind data in WPF notify Icon window

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");
}
}
}
}

How to Bind a column with a function which take the value of another column in parameter

For now, I have something like that (2 columns with dropboxes containing values independent from each other):
<xcdg:DataGridControl.Columns>
<xcdg:Column Title="A"
FieldName="A"
CellContentTemplate="{StaticResource ADT}"
GroupValueTemplate="{StaticResource ADT}"
Converter="{StaticResource AConverter}"
CellEditor="{StaticResource AEditor}"/>
<xcdg:Column Title="B"
FieldName="B"
CellContentTemplate="{StaticResource BDT}"
GroupValueTemplate="{StaticResource BDT}"
Converter="{StaticResource BConverter}"
CellEditor="{StaticResource BEditor}"/>
</xcdg:DataGridControl.Columns>
And I would like the B column to be a dropbox containing values depending on the value selected in the first column.
I don't know how to achieve that. I read about Binding.RelativeSource but I think it is not at all what I should use.
Thanks
I can think of two ways to do that, and since you didn't provide your exact case, i will provide a simple scenario and build my answer base on it,
let say you have a DataGrid with two editable columns (A and B), in the edit mode you can choose the A value from a combobox list, and then the B combobox will be filtered to show only the items whom their value starts with the A value for example, if A="aa", B should be {"aaaa","aabb"}, to implement that you need first a Model that represent the DataGrid Items
public class GridItem
{
public String A { get; set; }
public String B { get; set; }
}
In your codebehind / ViewModel define those properties (the DataGrid , and the comboboxes ItemSource Collections) :
private ObservableCollection<GridItem> _gridItemsCollection = new ObservableCollection<GridItem>()
{
new GridItem()
{
A="aa",
B="bbbb"
}
};
public ObservableCollection<GridItem> GridItemsCollection
{
get
{
return _gridItemsCollection;
}
set
{
if (_gridItemsCollection == value)
{
return;
}
_gridItemsCollection = value;
OnPropertyChanged();
}
}
//for the first Combobox
private ObservableCollection<String> _aCollection = new ObservableCollection<String>()
{
"aa",
"bb"
};
public ObservableCollection<String> ACollection
{
get
{
return _aCollection;
}
set
{
if (_aCollection == value)
{
return;
}
_aCollection = value;
OnPropertyChanged();
}
}
//for the second Combobox
private ObservableCollection<String> _bCollection ;
public ObservableCollection<String> BCollection
{
get
{
return _bCollection;
}
set
{
if (_bCollection == value)
{
return;
}
_bCollection = value;
OnPropertyChanged();
}
}
Define a full B collection from which your B combobox's itemsource will be populated
ObservableCollection<String> MainBCollection = new ObservableCollection<String>()
{
"aaaa",
"aabb",
"bbaa",
"bbbb"
};
Finally the population of the B combobox will be based on the selected item in the A combobox using this property,
private String _selectedAItem;
public String SelectedAItem
{
get
{
return _selectedAItem;
}
set
{
if (_selectedAItem == value)
{
return;
}
_selectedAItem = value;
OnPropertyChanged();
var returnedCollection = new ObservableCollection<String>();
foreach (var val in MainBCollection)
{
if (val.StartsWith(_selectedAItem))
{
returnedCollection.Add(value);
}
}
BCollection = new ObservableCollection<string>(returnedCollection);
}
}
You need of course to implement the INotifypropertyChanged Interface, so that the B Combobox Itemsource will be updated,
Now regarding the Xaml, due to some limitations in Xceed you need to specify the Combobox's ItemSource and SelectedItem using the RelativeSource and Ancestor binding,
<Grid >
<xcdg:DataGridControl ItemsSource="{Binding GridItemsCollection}" AutoCreateColumns="False" SelectionMode="Single" >
<xcdg:DataGridControl.Columns>
<xcdg:Column Title="A"
FieldName="A"
>
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
<xcdg:Column.CellEditor>
<xcdg:CellEditor>
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<ComboBox Name="AComboBox" SelectedItem="{Binding SelectedAItem, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}" SelectedValue="{xcdg:CellEditorBinding}"
ItemsSource="{Binding RelativeSource=
{RelativeSource FindAncestor,
AncestorType={x:Type wpfApplication3:MainWindow}},
Path=ACollection}">
</ComboBox>
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
</xcdg:CellEditor>
</xcdg:Column.CellEditor>
</xcdg:Column>
<xcdg:Column Title="B"
FieldName="B"
>
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
<xcdg:Column.CellEditor>
<xcdg:CellEditor>
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<ComboBox Name="AComboBox" SelectedValue="{xcdg:CellEditorBinding}" ItemsSource="{Binding RelativeSource=
{RelativeSource FindAncestor,
AncestorType={x:Type Window}},
Path=BCollection}">
</ComboBox>
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
</xcdg:CellEditor>
</xcdg:Column.CellEditor>
</xcdg:Column>
</xcdg:DataGridControl.Columns>
</xcdg:DataGridControl>
</Grid>
and the result is something like that
The Other way to do that is by using a MultivalueConverter and update the B Collection eachtime the A SelectedValue is changed,
something like that :
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<ComboBox Name="AComboBox" SelectedValue="{xcdg:CellEditorBinding}">
<ComboBox.ItemsSource>
<MultiBinding Converter="{StaticResource BCollectionConverter}">
<Binding Path="BCollection" RelativeSource="{RelativeSource AncestorType={x:Type Window}}"/>
<Binding Path="SelectedValue" ElementName="AComboBox" />
</MultiBinding>
</ComboBox.ItemsSource>
</ComboBox>
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
And implement the converter to update the B Combobox's ItemSource,
public class BCollectionConverter:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null)
return null;
var bCollection = (values[0] as ObservableCollection<String>);
var aSelectedItem = (values[1] as String);
if (aSelectedItem == null)
return null;
var returnedCollection = new ObservableCollection<String>();
foreach (var value in bCollection)
{
if (value.StartsWith(aSelectedItem))
{
returnedCollection.Add(value);
}
}
return returnedCollection;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I didn't try that last one, you might as well give it a try, I hope that did help.

Toggling BottomAppBar visibility not working?

I have a list of items - when one is selected, I'd like to slide the appbar up. This is what I have so far:
<AppBar IsOpen="{Binding BookIsSelected}">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<AppBarButton Icon="Remove" Label="Remove Book" Command="{Binding RemoveBook}" Visibility="{Binding BookIsSelected, Mode=TwoWay, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</Grid>
</AppBar>
In the gridview of books, I have this:
SelectedItem ="{Binding SelectedBook, Mode = TwoWay}" />
BookIsSelected:
private bool _bookSelected;
public bool BookIsSelected {
get {
return _bookSelected;
}
set {
SetProperty(ref _bookSelected, value);
}
}
I can see the get/set hit when I select a book, but the appbar never flies out. What am I doing wrong?
I can't see any obvious problems with the code that you've shown so far. However, you haven't shown the code for the SelectedBook property or SetProperty method, so perhaps your problem lies there? Your SelectedBook property should look something like this, setting BookIsSelected to true:
private YourDataType _selectedBook;
public YourDataType SelectedBook{
get {
return _selectedBook;
}
set {
SetProperty(ref _selectedBook, value);
BookIsSelected = true;
}
}
However, unless you set the BookIsSelected property back to false at some stage, this will only work once. Your SetProperty method should notify the INotifyPropertyChanged interface of property changes, as #Amer mentioned in the comments. It should look something like this example from the linked page:
private void SetProperty([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

Autocompletebox SelectedText Bug

I want to bind both the SelectedText and SelectedItem properties of an AutocompleteBox because my client wants to be able to input text and select from the list also. It's working properly but ...
The MainPage has one DataGrid. When I select a record from the Grid (i.e. SelectedItem), I want to set it in a popup window's AutocompleteBox. Some times it works but some times it doesn't.
What should I do for this issue?
This is my XAML:
<Sdk:AutoCompleteBox Grid.Column="3" Grid.Row="3" Height="18" Width="150"
IsTextCompletionEnabled="True" TabIndex="9" HorizontalAlignment="Left"
Text="{Binding ElementName=ResEdit,Path=DataContext.SelectedDemoText,Mode=TwoWay}"
ItemsSource="{Binding ElementName=ResEdit,Path=DataContext.DemoList,Mode=OneWay}"
ItemTemplate="{StaticResource DemoTemplate}"
ValueMemberPath="DemoCode"
LostFocus="AutoCompleteBox_LostFocus"
Margin="0,0,21,0" Padding="0">
</Sdk:AutoCompleteBox>
This property is in my view-model and bound to the DataGrid:
public InvoicesDTO SelectedInvoice
{
get { return _selectedInvoice; }
set
{
SelectedInvoice = value;
SelectedDomoText = SelectedInvoice.DemoText.Trim();
RaisePropertyChanged("SelectedInvoice");
}
}
You should not use both function SelectedText and SelectedItem in autocomplete. it's a bug of AutoCompleteBox..... A better way is to set the visiblity of the textbox and AutoCompleteBox on GotFocus and LossFocus. This Way You Will Defiantly Solve You Problem
private void DemoAutoComplete_LostFocus(object sender, RoutedEventArgs e)
{
DemoTextBox.Visibility = Visibility.Visible;
DemoAutoComplete.Visibility = Visibility.Collapsed;
DemoTextBox.Text = OCRAutoComplete.Text;
((DemoVM)this.DataContext).SelectedDemoText = DemoAutoComplete.Text;
}
private void DemoTextBox_GotFocus(object sender, RoutedEventArgs e)
{
DemoAutoComplete.Text = OctTextBox.Text;
DemoTextBox.Visibility = Visibility.Collapsed;
DemoAutoComplete.Visibility = Visibility.Visible;
DemoAutoComplete.Focus();
}