I have a problem with searching records. As you type characters, the list will not update.
At the moment I am using Binding Login for testing but I will be using Uri with a photo.
My ViewModel
private ObservableCollection<FollowerModel> _followerItems = null;
public ObservableCollection<FollowerModel> FollowerItems
{
get => _followerItems;
set
{
_followerItems = value;
OnPropertyChanged();
OnPropertyChanged();
}
}
private FollowerModel _followerItem = null;
public FollowerModel FollowerItem
{
get => _followerItem;
set
{
_followerItem = value;
OnPropertyChanged();
OnFollowerItemChanged();
}
}
// ...
private void OnSearchPhrase()
{
var searchPhrase = _searchPhrase.Trim();
if (searchPhrase.Length < SEARCH_PHRASE_MIN_LENGTH) return; // SEARCH_PHRASE_MIN_LENGTH = 3
FollowerItems.Where(name => name.Login.Contains(searchPhrase));
}
My PageView
<flv:FlowListView SeparatorVisibility="None" HasUnevenRows="true" FlowItemTappedCommand="{Binding FollowerItemCommand}" FlowColumnMinWidth="100" FlowItemsSource="{Binding FollowerItems}">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<Grid Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="98" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="98" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Login}" />
</Image>-->
</Grid>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
First add a search bar in your xaml
<SearchBar Placeholder="Search" Text="{Binding SearchText}" />
Then Create a Bindable property for it
private string _searchText;
public string SearchText
{
get { return _searchText; }
set
{
_searchText = value;
SearchUserText(_searchText);
RaisePropertyChanged("SearchText");
}
}
Then Create a function SearchUserText that will listen for text and change your list
public void SearchUserText(string text)
{
var searchPhrase = _searchPhrase.Trim();
if (searchPhrase.Length < SEARCH_PHRASE_MIN_LENGTH) return; // SEARCH_PHRASE_MIN_LENGTH = 3
FollowerItems.Where(name => name.Login.Contains(searchPhrase));
}
NB: Don't forget to add getters and setters on your model FollowerModel
example:
public class FollowerModel : BindableBase
{
private int _id;
public int Id
{
get { return _id; }
set { SetProperty(ref _id, value); }
}
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
}
First of all, you will need two ObservableCollections:
One ObservableCollection which contains all items
One ObservableCollection which contains the filtered items
The filtered ObservableCollection will be the one which is binded to your control. So you only need a private variable for all items and a public property for the filtered items:
private ObservableCollection<FollowerModel> _allFollowerItems;
private ObservableCollection<FollowerModel> _followerItems;
public ObservableCollection<FollowerModel> FollowerItems
{
get { return _followerItems}
set
{
_followerItems = value;
RaisePropertyChanged();
}
}
One more thing I noticed is that you are using OnPropertyChanged(). It is deprecated, use RaisePropertyChanged() instead.
In your OnSearchPhrase() method, you want to set FollowerItems to the original items (_allFollowerItems) if searchPhrase.Length < SEARCH_PHRASE_MIN_LENGTH. Otherwise, you will filter it:
private void OnSearchPhrase()
{
var searchPhrase = _searchPhrase.Trim();
if (searchPhrase.Length < SEARCH_PHRASE_MIN_LENGTH)
{
FollowerItems = _allFollowerItems;
}
else
{
FollowerItems = new ObservableCollection<FollowerModel>(_allFollowerItems.Where(name => name.Login.ToLower().Contains(searchPhrase.ToLower())));
}
}
Notice that I added ToLower() to the strings to have a more user friendly search. If Case-sensetive search was intended, just remove it.
Don't forget to set FollowerItems and _allFollowerItems once when at the place where you are loading your items right now:
FollowerItems = new ObservableCollection(items);
_allFollowerItems = new ObservableCollection(items);
Related
I have a WPF custom Control in which I have a Listview. The control has dependancy properties for the ItemSource and ItemTemplate of the ListView. This all works fine. What I would like to do is to be able to set a default ItemTemplate so that I don't end up with object.ToString() for the Items in the Listview.
Below is the Xaml Style for my control.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:btl="clr-namespace:Btl.Controls"
mc:Ignorable="d">
<DataTemplate x:Key="DefaultListViewItem" DataType="btl:SelectableItem">
<StackPanel Orientation="Horizontal">
<CheckBox Margin="2" IsChecked="{Binding Selected}" />
<TextBlock Margin="5,2" Text="{Binding Description}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
<Style TargetType="{x:Type btl:SelectItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type btl:SelectItemsControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="PART_Title" Margin="10"
Text="{Binding Path=Title,
RelativeSource={RelativeSource TemplatedParent}}"
TextWrapping="Wrap"
Visibility="Collapsed"/>
<GroupBox Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="PART_EnabledCheck" Margin="0,5"
Content=""
IsChecked="{Binding Path=EnabledCheck, Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock x:Name="PART_GroupTitle" VerticalAlignment="Center"
Text="{Binding Path=GroupTitle,
RelativeSource={RelativeSource TemplatedParent}}"/>
</StackPanel>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<ListView x:Name="PART_Items"
ItemsSource="{Binding ItemSourceList,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type btl:SelectItemsControl}}}"
ItemTemplate="{Binding ItemTemplate,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type btl:SelectItemsControl}}}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="2"
>
</ListView>
</DockPanel>
<CheckBox x:Name="PART_SelectAllCheck" Grid.Row="1" Margin="11,5" Content="Select All"
IsChecked="{Binding Selected}"/>
</Grid>
</GroupBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is my control
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace Btl.Controls
{
/// <summary>
///
/// </summary>
///
[TemplatePart(Name = "PART_Title", Type = typeof(TextBlock))]
[TemplatePart(Name = "PART_EnabledCheck", Type = typeof(CheckBox))]
[TemplatePart(Name = "PART_GroupTitle", Type = typeof(TextBlock))]
[TemplatePart(Name = "PART_Items", Type = typeof(ListView))]
[TemplatePart(Name = "PART_SelectAllCheck", Type = typeof(CheckBox))]
public class SelectItemsControl : UserControl
{
#region DependencyProperties
#region Title
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(SelectItemsControl), new PropertyMetadata(string.Empty,
OnTitleChanged
));
private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as TextBlock;
if (control != null)
if (string.IsNullOrEmpty(e.NewValue.ToString()))
{
control.Visibility = string.IsNullOrEmpty(e.NewValue.ToString()) ? Visibility.Collapsed : Visibility.Visible;
}
}
#endregion
#region HasEnabledCheck
public bool HasEnabledCheck
{
get { return (bool)GetValue(HasEnabledCheckProperty); }
set { SetValue(HasEnabledCheckProperty, value); }
}
// Using a DependencyProperty as the backing store for EnabledCheck. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HasEnabledCheckProperty =
DependencyProperty.Register("HasEnabledCheck", typeof(bool), typeof(SelectItemsControl), new UIPropertyMetadata(false));
#endregion
#region EnabledCheck
public bool EnabledCheck
{
get { return (bool)GetValue(EnabledCheckProperty); }
set { SetValue(EnabledCheckProperty, value); }
}
// Using a DependencyProperty as the backing store for EnabledCheck. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EnabledCheckProperty =
DependencyProperty.Register("EnabledCheck", typeof(bool), typeof(SelectItemsControl), new UIPropertyMetadata(true));
#endregion
#region GroupTitle
public string GroupTitle
{
get { return (string)GetValue(GroupTitleProperty); }
set { SetValue(GroupTitleProperty, value); }
}
// Using a DependencyProperty as the backing store for GroupTitle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty GroupTitleProperty =
DependencyProperty.Register("GroupTitle", typeof(string), typeof(SelectItemsControl), new UIPropertyMetadata(""));
#endregion
#region ItemSourceList
public IEnumerable<ISelectable> ItemSourceList
{
get { return (IEnumerable<ISelectable>)GetValue(ItemSourceListProperty); }
set { SetValue(ItemSourceListProperty, value); }
}
// Using a DependencyProperty as the backing store for Items. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemSourceListProperty =
DependencyProperty.Register("ItemSourceList", typeof(IEnumerable), typeof(SelectItemsControl));
#endregion
#region ItemTemplate
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(SelectItemsControl),
new UIPropertyMetadata(default(DataTemplate)));
#endregion
#region DescriptionTemplate
public DataTemplate DescriptionTemplate
{
get { return (DataTemplate)GetValue(DescriptionTemplateProperty); }
set { SetValue(DescriptionTemplateProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DescriptionTemplateProperty =
DependencyProperty.Register("DescriptionTemplate", typeof(DataTemplate), typeof(SelectItemsControl),
new UIPropertyMetadata(default(DataTemplate), OnDescriptionTemplateChanged));
private static void OnDescriptionTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
#endregion
#region ItemSelected
public bool ItemSelected
{
get { return (bool)GetValue(ItemSelectedProperty); }
set { SetValue(ItemSelectedProperty, value); }
}
// Using a DependencyProperty as the backing store for EnabledCheck. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemSelectedProperty =
DependencyProperty.Register("ItemSelectedCheck", typeof(bool), typeof(SelectItemsControl), new UIPropertyMetadata(false));
#endregion
#region ItemDescription
public string ItemDescription
{
get { return (string)GetValue(ItemDescriptionProperty); }
set { SetValue(ItemDescriptionProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemDescription. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemDescriptionProperty =
DependencyProperty.Register("ItemDescription", typeof(string), typeof(SelectItemsControl), new UIPropertyMetadata(""));
#endregion
#region SelectAllCheck
public bool SelectAll
{
get { return (bool)GetValue(SelectAllProperty); }
set { SetValue(SelectAllProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectAll. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectAllProperty =
DependencyProperty.Register("SelectAll", typeof(bool), typeof(SelectItemsControl), new UIPropertyMetadata(false));
#endregion
#endregion
#region Private Members
private TextBlock _partTitle;
private CheckBox _partEnabledCheck;
private TextBlock _partGroupTitle;
private ListView _partItemsListView;
private CheckBox _partSelectAllCheck;
#endregion
/// <summary>
///
/// </summary>
static SelectItemsControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SelectItemsControl),
new FrameworkPropertyMetadata(typeof(SelectItemsControl)));
}
public SelectItemsControl()
{
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
if (ItemTemplate == null)
{
CreateDefaultItemTemplate();
}
PresentationSource presentationSource = PresentationSource.FromVisual((Visual)sender);
// Subscribe to PresentationSource's ContentRendered event
// ReSharper disable once PossibleNullReferenceException
presentationSource.ContentRendered += SelectItemsControl_ContentRendered;
}
private void SelectItemsControl_ContentRendered(object sender, EventArgs e)
{
// Don't forget to unsubscribe from the event
((PresentationSource)sender).ContentRendered -= SelectItemsControl_ContentRendered;
ListenToSelectedCheckBoxClickEvent(_partItemsListView, true);
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
ListenToSelectedCheckBoxClickEvent(_partItemsListView, false);
}
private void CreateDefaultItemTemplate()
{
DataTemplate template = new DataTemplate { DataType = typeof(ListViewItem) };
FrameworkElementFactory stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
FrameworkElementFactory selected = new FrameworkElementFactory(typeof(CheckBox));
selected.SetBinding(TextBlock.TextProperty, new Binding("Selected"));
stackPanelFactory.AppendChild(selected);
FrameworkElementFactory title = new FrameworkElementFactory(typeof(TextBlock));
title.SetBinding(TextBlock.TextProperty, new Binding("Description"));
stackPanelFactory.AppendChild(title);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Code to get the Template parts as instance member
_partTitle = GetTemplateChild("PART_Title") as TextBlock;
_partEnabledCheck = GetTemplateChild("PART_EnabledCheck") as CheckBox;
_partGroupTitle = GetTemplateChild("PART_GroupTitle") as TextBlock;
_partItemsListView = GetTemplateChild("PART_Items") as ListView;
//_partItemSelectedCheck = GetTemplateChild("PART_ItemSelectedCheck") as CheckBox;
_partSelectAllCheck = GetTemplateChild("PART_SelectAllCheck") as CheckBox;
if (_partTitle == null || _partEnabledCheck == null || _partGroupTitle == null || _partItemsListView == null ||
_partSelectAllCheck == null)
{
throw new NullReferenceException("Template parts not available");
}
// set visibility
_partEnabledCheck.Visibility = HasEnabledCheck ? Visibility.Visible : Visibility.Collapsed;
_partEnabledCheck.Click += PartEnabledCheckOnClick;
_partTitle.Visibility = string.IsNullOrEmpty(_partTitle.Text) ? Visibility.Collapsed : Visibility.Visible;
_partGroupTitle.Visibility = string.IsNullOrEmpty(_partGroupTitle.Text) ? Visibility.Collapsed : Visibility.Visible;
_partSelectAllCheck.Click += PartSelectAllCheckOnClick;
}
private void PartEnabledCheckOnClick(object sender, RoutedEventArgs routedEventArgs)
{
_partItemsListView.IsEnabled = EnabledCheck;
_partSelectAllCheck.IsEnabled = EnabledCheck;
}
private void ListenToSelectedCheckBoxClickEvent(DependencyObject parent, bool set)
{
foreach (CheckBox cb in VisualTreeHelpers.FindVisualChildren<CheckBox>(parent))
{
BindingExpression binding = cb.GetBindingExpression(CheckBox.IsCheckedProperty);
// ReSharper disable once PossibleNullReferenceException
if (binding.ParentBinding.Path.Path == "Selected")
{
if (set)
cb.Click += SelectedCheckBox_Click;
else
cb.Click -= SelectedCheckBox_Click;
}
}
}
private void SelectedCheckBox_Click(object sender, RoutedEventArgs e)
{
_partSelectAllCheck.IsChecked = !ItemSourceList.AsQueryable().Any(x => x.Selected == false);
}
private void PartSelectAllCheckOnClick(object sender, RoutedEventArgs routedEventArgs)
{
foreach (CheckBox cb in VisualTreeHelpers.FindVisualChildren<CheckBox>(_partItemsListView))
{
BindingExpression binding = cb.GetBindingExpression(CheckBox.IsCheckedProperty);
// ReSharper disable once PossibleNullReferenceException
if (binding.ParentBinding.Path.Path == "Selected")
{
cb.IsChecked = _partSelectAllCheck.IsChecked ?? false;
}
}
}
}
}
Could someone please post some code which shows how set - create the default template?
This turned out to be simpler than I thought. Because the ItemTemplate is bound to a dependency property I can specify the default template there. That just left the creation of the template. See below.
#region ItemTemplate
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(SelectItemsControl),
new UIPropertyMetadata(DefaultItemTemplate));
private static DataTemplate DefaultItemTemplate
{
get
{
// tried using a MemoryStream - StreamWriter but was getting a
// "Root element missing error", would be nice to know why.
var sb = new StringBuilder();
sb.Append("<DataTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">");
sb.Append("<StackPanel Orientation=\"Horizontal\">");
sb.Append("<CheckBox Margin=\"2\" IsChecked=\"{Binding Selected}\" />");
sb.Append("<TextBlock Margin=\"5,2\" Text=\"{Binding Description}\" VerticalAlignment=\"Center\"/>");
sb.Append("</StackPanel>");
sb.Append("</DataTemplate>");
var myByteArray = System.Text.Encoding.UTF8.GetBytes(sb.ToString());
var ms = new MemoryStream(myByteArray);
return (DataTemplate) XamlReader.Load(ms);
}
}
#endregion
I am having an issue with list views on in a couple of my Xamarin Forms applications. One form is within a tabbed page setup, the other is a normal content page (different apps)
I have a class like this
public class SomeClass
{
public string StringOne {get;set;}
public string StringTwo {get;set;}
public int IntOne {get;set;}
}
In my Content page, I set up an ObservableCollection and add some data in. I then tell the list that SomeClass is my ItemSource. This produces the ListView correctly on all of my devices.
The problem is that when I change one of the properties, nothing on the ListView changes (so if say I have 3 objects in the Observable and remove one, the list still says 3 - or if I change a property in my second object, the second item on the ListView doesn't change either).
I have also tried to solve the problem by using a standard List and implement INotifyChanged within the class. Again though, the ListView doesn't alter when the List changes.
I know the data has altered as if I make a change to the object, come out and go back in, the data has changed in the UI.
Am I doing something wrong or is this a bug I need to putting into Bugzilla?
It will not change if you don't bind it and implement INotifyPropertyChanged interface.
Sample Code:
public class ObservableProperty : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class SomeClass:ObservableProperty
{
string stringOne;
string stringTwo;
int intOne;
public string StringOne
{
get{return stringOne;}
set
{
stringOne = value;
OnPropertyChanged("StringOne");
}
}
public string StringTwo
{
get{ return stringTwo;}
set
{
stringTwo = value;
OnPropertyChanged("StringTwo");
}
}
public int IntOne
{
get{ return intOne;}
set
{
intOne = value;
OnPropertyChanged("IntOne");
}
}
}
public class MainVM:ObservableProperty
{
ObservableCollection<SomeClass> items;
public ObservableCollection<SomeClass> items
{
get{return items;}
set
{
items = value;
OnPropertyChanged("Items");
}
}
public MainVM()
{
Items = new ObservableCollection<SomeClass>();
Items.Add(new SomeClass(){StringOne = "123", StringTwo = "test", IntOne =12});
}
public void CallMeForChangingProperty()
{
SomeClass item = Items[0];
item.StringOne = "Test1";
}
}
public class MainView
{
public MainView()
{
this.BindingContext= new MainVM()
}
}
< ListView ItemsSource="{Binding Items}" RowHeight="120">
< ListView.ItemTemplate>
< DataTemplate>
< ViewCell>
< ViewCell.View>
< StackLayout>
< Label Text= "StringOne" />
< Label Text= "StringTwo" />
< Label Text= "IntOne" />
</ StackLayout>
</ ViewCell.View>
</ ViewCell>
</ DataTemplate>
</ ListView.ItemTemplate>
</ ListView>
Answer given by #eakgul works like a charm for me.
I'll attach here what I've implemented, maybe it could help someone.
You have to set INotifyPropertyChanged both, to the ObservableColection and to it's itens.
I have a BaseViewModel with INotifyPropertyChanged as follows:
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void SetProperty<T>(ref T backingField, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals( backingField, value)) return;
backingField = value;
OnPropertyChanged(propertyName);
}
}
On my BluetoothPage.xaml, first I set bindincontext to my BluetoothPageViewModel.cs and set the ListView ItemsSource and it's binded labels:
<ContentPage.BindingContext>
<viewmodel:BluetoothPageViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout Padding="5,10">
<Button x:Name="Scan_Devices_Button"
Command="{Binding SearchNew_Button_Clicked}"/>
<ListView x:Name="DevicesList"
ItemsSource="{Binding BluetoothDevices}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
IsPullToRefreshEnabled="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Text="{Binding device.Device.NativeDevice.Name}"/>
<Label Grid.Column="1"
Text="{Binding device.Device.NativeDevice.Address, StringFormat='ID: {0}'}"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Text="{Binding device.Rssi, StringFormat='Power: {0:F2}dbm'}"/>
<Label Grid.Column="1"
Text="{Binding distance, StringFormat='Distance: {0:F2}m'}"/>
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
Then, in my BluetoothPageViewModel.cs I extend it with the BaseViewModel and declare ItemsSource BluetoothDevices with INotifyPropertyChanged. At this moment, everytime I change an item on the ObservableCollection BluetoothDevices, the ListView will be updated. But, If I made a change on an item inside the ObservableCollection, nothing will happen!
So, you must set INotifyPropertyChanged to it's itens.
Following is my BluetoothPageViewModel, which uses a class BluetoothPageModel in the PageModel BluetoothPageModel.cs
The BluetoothPageViewModel:
public class BluetoothPageViewModel : BaseViewModel
{
public BluetoothPageViewModel()
{
SearchNew_Button_Clicked = new Command(NewDevices_Button_Clicked_Event);
Scan_Devices_Button_BgColor = "#D6D7D7";
Scan_Devices_Button_Text = "Scan nearby devices";
}
#region Declarations
public List<IDevice> iDeviceList = new List<IDevice>();
public ObservableCollection<BluetoothPageModel> _bluetoothDevices = new ObservableCollection<BluetoothPageModel>();
public BluetoothPageModel _selectedItem;
public ObservableCollection<BluetoothPageModel> BluetoothDevices
{
get { return _bluetoothDevices; }
set { SetProperty(ref _bluetoothDevices, value); }
}
public BluetoothPageModel SelectedItem
{
get { return _selectedItem; }
set { SetProperty(ref _selectedItem, value); }
}
public ICommand SearchNew_Button_Clicked { get; private set; }
#endregion
#region Functions
private void NewDevices_Button_Clicked_Event(object obj)
{
// discover some devices
if (!CrossBleAdapter.Current.IsScanning)
{
BluetoothDevices.Clear();
iDeviceList.Clear();
var scanner = CrossBleAdapter.Current.Scan().Subscribe(scanResult =>
{
if (!iDeviceList.Contains(scanResult.Device))
{
iDeviceList.Add(scanResult.Device);
Device.BeginInvokeOnMainThread(() =>
{
BluetoothDevices.Add(new BluetoothPageModel
{
device = scanResult,
distance = Math.Pow(10, ((-68 - scanResult.Rssi) / 31.1474))
});
});
}
else
{
int ind = iDeviceList.IndexOf(scanResult.Device);
Device.BeginInvokeOnMainThread(() =>
{
BluetoothDevices[ind].device = scanResult;
BluetoothDevices[ind].distance = Math.Pow(10, ((-68 - scanResult.Rssi) / 31.1474));
});
}
});
}
else
{
CrossBleAdapter.Current.StopScan(); //When you want to stop scanning
}
}
#endregion
}
Finally, to be able to update data when you change a property of the BluetoothPageModel class:
public class BluetoothPageModel:BaseViewModel
{
public IScanResult _device;
public double _distance;
public IScanResult device
{
get { return _device; }
set { SetProperty(ref _device, value); }
}
public double distance
{
get { return _distance; }
set { SetProperty(ref _distance, value); }
}
}
Thanks to eakgul answer I could get it working. Hope it can help someone else.
I have an observable collection OBSCollection which i am parsing. In this collection i am checking if the name property is "critical" and if it is "critical" , then i am trying to create red buttons dynamically for each occurence of property.
if (OBSCollection.Any(p => p.Name == "Critical"))
{
criticalcount = OBSCollection.Where(x => x.Name == "Critical").ToList().Count;
for (int i = 0; i < criticalcount; i++)
{
Button temp = new Button();
temp.Background = new SolidColorBrush(Windows.UI.Colors.Red);
temp.Width = 200;
temp.Height = 100;
temp.Content = "Critical";
CriticalPanel.Children.Add(temp);
temp.Tapped += new TappedEventHandler(bTapped_Tapped);
}
private void bTapped_Tapped(object sender, TappedRoutedEventArgs e)
{
var toremovecritical = OBSCOllection.Where(x => x.Name == "critical").First();
uiElements.Remove(toremovecritical);
}
Now the above code works only if there is one occurrence of "Critical" property. How can i rewrite the code to work for multiple occurrences and hence create multiple buttons?
Also after displaying the buttons , if a user clicks on a button the buttons visible property should be collapsed and that particular item should be removed from observable collection. I am able to remove the button from observable collection but i cannot set the visibility property of button to false from bTapped_Tapped handler. Is there anyway to resolve this?
Here is a REALLY basic example using MVVM.
XAML:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<GridView ItemsSource="{Binding Things}">
<GridView.ItemTemplate>
<DataTemplate>
<Button Width="100" Height="100" Background="Red" Content="{Binding Name}" Click="CriticalClick" DataContext="{Binding}"></Button>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
ViewModel & Model:
public class ThingViewModel
{
public ObservableCollection<Thing> Things { get; set; }
public ThingViewModel()
{
var allThings = new List<Thing>();
for (int i = 0; i < 30; i++)
{
if (i % 2 == 0)
allThings.Add(new Thing { Name = "Critical" });
else
allThings.Add(new Thing { Name = "`NonCritical" });
}
this.Things = new ObservableCollection<Thing>(allThings.Where(x => x.Name == "Critical"));
}
}
public class Thing
{
public string Name { get; set; }
}
Code Behind:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.DataContext = new ThingViewModel();
}
private void CriticalClick(object sender, RoutedEventArgs e)
{
var tappedThing = (Thing) ((Button) sender).DataContext;
((ThingViewModel) this.DataContext).Things.Remove(tappedThing);
}
This is a really simple example of how to do it with binding and MVVM. It gives you a starting point for what you are asking for though.
Hope it helps.
my cpde in ModelView :
public Boolean EnableTextBox { get; set; }
public CustomerAccountVM()
{
this.EnableTextBox = false;
//...
}
code in View:
XAML :
<TextBox Text="{Binding Path=IdCustomer, Mode=Default}" IsEnabled="{Binding Path=EnableTextBox,Mode=Default}" />
Why the code does not work?
no answer ?
You're not publishing the fact that the Enable property has been updated.
You need to implement the INotifyPropertyChanged interface and change your property to be:
private Boolean _enableTextBox;
public Boolean EnableTextBox
{
get { return _enableTextBox; }
set
{
_enableTextBox = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
You should wrap the PropertyChanged code in a method so you're not repeating yourself.
I have a label in WPF4 and trying to bind the content to a value from c# class. I have created an ObjectDataProvider but for some reason can't see the content or updates. Can you point me to what I'm doing wrong?
Here is the xaml -
<Grid.Resources>
<local:SummaryData x:Key="mySummaryData"/> </Grid.Resources>
<Label Name ="lbCurrentVerValue" Grid.Row="1" Grid.Column="2" Content="{Binding Path=frameworkVersion, Source={StaticResource mySummaryData}}"/>
<TextBox Name ="lbFromVerValue" Grid.Row="2" Grid.Column="2" Text="{Binding Source={StaticResource mySummaryData}, Path=frameworkVersion}"/>
and here is the c# code-
namespace DBUpgradeUI
{
public partial class DBUpgReadinessCheck : Window
{
public string userConnStr = String.Empty;
public string userFoldPath = String.Empty;
public SummaryData sd = new SummaryData();
public DBUpgReadinessCheck()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ReadinessCheck(userConnStr, userFoldPath);
}
public void ReadinessCheck(string connectionString, string folderPath)
{
FrmImportUtility frmWork = new FrmImportUtility();
sd.frameworkVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); ;
frmWork.CurrentlyProcessingVersion(connectionString, folderPath, ref sd.currentVersion, ref sd.finalVersion);
}
}
public class SummaryData
{
public string currentVersion = "Test";
public string finalVersion = "finalVerTest";
public string frameworkVersion = String.Empty;
}
}
You must implement INotifyPropertyChanged in SummaryData:
public class SummaryData:INotifyPropertyChanged
{
private string currentVersion = "Test";
public string CurrentVersion
{
get { return currentVersion; }
set {
currentVersion = value;
NotifyChanged("CurrentVersion");
}
}
private void NotifyChanged(string p)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(property));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
After trying ways to bind properties from a class object without having dependency properties and having no luck, I have done the following and it works like a charm!
xaml -
<Label Name ="lbCurrentVerValue" Grid.Row="1" Grid.Column="2" Content="{Binding Mode=OneWay, ElementName=step2Window,Path=currentVersion}"/>
<Label Name ="lbFromVerValue" Grid.Row="2" Grid.Column="2" Content="{Binding Mode=OneWay, ElementName=step2Window,Path=finalVersion}"/>
and c# code -
public partial class DBUpgReadinessCheck : Window
{
//Values being passed by step1 window
public string userConnStr = String.Empty;
public string userFoldPath = String.Empty;
//Dependency properties
public string currentVersion
{
get { return (String)this.GetValue(CurVersionProperty); }
set { this.SetValue(CurVersionProperty, value); }
}
public static readonly DependencyProperty CurVersionProperty =
DependencyProperty.Register("currentVersion", typeof(String), typeof(DBUpgReadinessCheck), new UIPropertyMetadata("0"));
public String finalVersion
{
get { return (String)GetValue(FinVerProperty); }
set { SetValue(FinVerProperty, value); }
}
public static readonly DependencyProperty FinVerProperty =
DependencyProperty.Register("finalVersion", typeof(String), typeof(DBUpgReadinessCheck), new UIPropertyMetadata("0"));
public string frameworkVersion
{
get { return (String)GetValue(FrameWrkVersionProperty); }
set { SetValue(FrameWrkVersionProperty, value); }
}
public static readonly DependencyProperty FrameWrkVersionProperty =
DependencyProperty.Register("frameworkVersion", typeof(String), typeof(DBUpgReadinessCheck), new UIPropertyMetadata("0"));
public DBUpgReadinessCheck()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ReadinessCheck(userConnStr, userFoldPath);
}
public void ReadinessCheck(string connectionString, string folderPath)
{
FrmImportUtility frmWork = new FrmImportUtility();
frameworkVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
string tempCurVersion = string.Empty;
String tempFinalVersion = string.Empty;
frmWork.CurrentlyProcessingVersion(connectionString, folderPath, ref tempCurVersion, ref tempFinalVersion);
currentVersion = tempCurVersion;
finalVersion = tempFinalVersion;
//Validation
if (finalVersion == frameworkVersion)
{
string strUri2 = String.Format(#"pack://application:,,,/GreenCheck.jpg");
ImgFrmVersion.Source = new BitmapImage(new Uri(strUri2));
yelloExclamation.Visibility = System.Windows.Visibility.Hidden;
errMsg.Visibility = System.Windows.Visibility.Hidden;
succMsg.Visibility = System.Windows.Visibility.Visible;
btRecheckSetUp.Visibility = System.Windows.Visibility.Hidden;
btStartUpgrade.IsEnabled = true;
}
}
This works great. Thanks everyone for your help.