Capturing ReorderMode for ListView - WP8.1 - c#

I need to know when the ListView comes out of reorder mode. When I long press on the listview, I'm enabling the ReorderMode. After the reordering is done, I press hardware back button and the listview comes out reorder mode. Is there any way to check capture that?
I have tried data binding the ReorderMode property. But it does not capture the ReOrderMode disabled state.
xaml
<ListView x:Name="FolderListView" ItemsSource="{Binding}"
SelectionMode="None"
IsTapEnabled="True"
Tapped="ListItemTapped" AllowDrop="True" CanDragItems="True"
IsSwipeEnabled="True" Holding="OnItemHold"
ReorderMode="{Binding ReorderMode, Mode=TwoWay}">
code-behind
private void OnItemHold(object sender, HoldingRoutedEventArgs e)//Long Press on ListView
{
FolderListView.ReorderMode = ListViewReorderMode.Enabled;
ReorderModeChanged();
}
public ListViewReorderMode ReorderMode
{
get
{
return _reorderMode;
}
set
{
if (_reorderMode != value)
{
_reorderMode = value;
OnPropertyChanged("ReorderMode");
this.ReorderModeChanged();
}
}
}
private void ReorderModeChanged()
{
DBManager dbMan = DBManager.Instance;
if (this.ReorderMode == ListViewReorderMode.Enabled)
{
dbMan.IsReorderEnabled = true;
}
else
{
dbMan.IsReorderEnabled = false;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Any ideas/work arounds?

I think you have wrong DataContext for your ListView. You bind some collection to ListView, but this collection does not have ReorderMode property. You can look for binding errors in Output window while debugging your app.

Related

Silverlight: ComboboxItem text not updated when binding changes

I have an issue whereby when I update the text on a Comboboxitem it isnt instantly reflected on the UI. One has to click the Combobox to show the items (which have the correct text). Any ideas why? Note this exact code works perfectly in WPF
The property that defines the string to show
public string NormallyOpenString
{
get
{
if (this.IsInput)
{
return "High";
}
else if (this.IsRelay)
{
return "Open";
}
else
{
return "Open (High)";
}
}
}
Which is bound to a Combobox like so
<ComboBox SelectedIndex="{Binding Normally, Mode=TwoWay}" >
<ComboBoxItem Content="{Binding NormallyOpenString}" />
<ComboBoxItem Content="{Binding NormallyClosedString}" />
</ComboBox>
When another combobox changes I want to update the text as it changes what IsInput / IsRelay is. I do this via NotifyPropertyChanged like so
this.NotifyPropertyChanged("NormallyOpenString");
this.NotifyPropertyChanged("NormallyClosedOpenString");
this.NotifyPropertyChanged("Normally");
I've never done it that way, so I can't vouch for it. This is how I do property change notification:
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
private string normallyOpenString = "I'm an open string!";
public string NormallyOpenString
{
get { return normallyOpenString; }
set
{
normallyOpenString = value;
RaisePropertyChanged("NormallyOpenString");
}
}
}
So now, whenever anyone calls your setter, anything bound to your property will be updated. So if it gets set from one binding, all other bindings that are also bound to it will be updated.
I think you should rather use the SelectedItem property.

Combobox not refreshing after adding items C# WPF

In my main window I have a class where I store all my data in ( called "Measconsettings"). This class ("Measconsettings") contains an ObservableCollection "DeviceDefinitionList" of an other class "DeviceDefinition"
ObservableCollection<DeviceDefinition> DeviceDefinitionList.
When I press a button in my MainWindow a new window is created with datacontex = Measconsettings.
In this new window there is a combobox which ItemsSource is bound to "Measconsettings.DeviceDefinitionList".
<ComboBox Grid.Row="2" Grid.Column="2" Margin="2,0" Name="CboDeviceDefinitionList" ItemsSource="{Binding Path=DeviceDefinitionList}"/>
My problem now is that the combobox does not update automaticly when an item is added.
I need to close the new window and press the button again to open it and only then it shows the items in the combobox.
I tried adding CboDeviceDefinitionList.Items.Refresh(); but it does not work..
Only when I add CboDeviceDefinitionList.ItemsSource = orgMeasConSettings.DeviceDefinitionList; right after adding items to the ObservableCollection then they show up right away.
Any ideas or tips on how to properly bind to a combobox? PS: I'm wpf beginner
This is my solution after implementing INotifyPropertyChanged as suggested by manish.
EDIT: It now works!
public class MeasConSettings : INotifyPropertyChanged
{
private ObservableCollection<DeviceDefinition> mDeviceDefinitionList;
private DeviceDefinition mSelectedDeviceDefinition;
public ObservableCollection<DeviceDefinition> DeviceDefinitionList
{
get
{
return mDeviceDefinitionList;
}
set
{
mDeviceDefinitionList = value;
}
}
public DeviceDefinition SelectedDeviceDefinition
{
get
{
return mSelectedDeviceDefinition;
}
set
{
mSelectedDeviceDefinition = value;
NotifyPropertyChanged("SelectedDeviceDefinition");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
XAML CODE:
<ComboBox Grid.Row="2" Grid.Column="2" Margin="2,0" Name="CboDeviceDefinitionList" ItemsSource="{Binding Path=DeviceDefinitionList}" SelectedItem="{Binding Path=SelectedDeviceDefinition}"/>
CODE for adding item:
orgMeasConSettings.DeviceDefinitionList.Clear();
foreach (DeviceDefinition deviceDefinition in newSettings.DeviceDefinitionList)
{
orgMeasConSettings.DeviceDefinitionList.Add(deviceDefinition);
}
orgMeasConSettings.SelectedDeviceDefinition = newSettings.DeviceDefinitionList.FirstOrDefault();

FlipView.SelectedIndex Mouse Mode vs Basic Touch Mode

I have a databound FlipView on my page that works great. I have 2 Buttons on my page that I want to use to navigate the FlipView forward and back. The buttons work when I use Mouse Mode in the Simulator or actually use my mouse in Local Machine mode.
But when I use 'Basic touch mode' or use my app on an actual touch screen and tap the buttons, the FlipView navigates to next view but then flashes/flips back.
Here is my FlipView:
<FlipView x:Name="Flipper" ItemsSource="{Binding Path=Views}">...</FlipView>
And my Button:
<Button x:Name="ForwardButton" IsEnabled="{Binding CanGoForward}" Style="{StaticResource ForwardButtonStyle}" Tapped="ForwardButton_OnTapped" />
And my event:
private void ForwardButton_OnTapped(object sender, TappedRoutedEventArgs e)
{
if (Flipper.SelectedIndex >= Flipper.Items.Count - 1)
{
Flipper.SelectedIndex = 0;
}
else
{
Flipper.SelectedIndex++;
}
e.Handled = true;
}
Update:
Adding OnSelectionChanged to the FlipView, I'm seeing it hit twice in Basic touch mode, but in Mouse mode only once, again Mouse mode works as expected.
Yeah, so I implemented it like this:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
ViewModel ViewModel { get { return this.DataContext as ViewModel; } }
private void Button_Click_1(object sender, RoutedEventArgs e)
{
this.ViewModel.MovePrevious();
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
this.ViewModel.MoveNext();
}
}
public class ViewModel : INotifyPropertyChanged
{
public ViewModel() { SelectedThing = ThingList.First(); }
List<int> m_ThingList = Enumerable.Range(1, 50).ToList();
public List<int> ThingList { get { return m_ThingList; } set { SetProperty(ref m_ThingList, value); } }
int m_SelectedThing = default(int);
public int SelectedThing { get { return m_SelectedThing; } set { SetProperty(ref m_SelectedThing, value); } }
internal void MovePrevious()
{
var _CurrentIndex = ThingList.IndexOf(this.SelectedThing);
try { SelectedThing = ThingList[--_CurrentIndex]; }
catch { SelectedThing = ThingList.First(); }
}
internal void MoveNext()
{
var _CurrentIndex = ThingList.IndexOf(this.SelectedThing);
try { SelectedThing = ThingList[++_CurrentIndex]; }
catch { SelectedThing = ThingList.Last(); }
}
public event PropertyChangedEventHandler PropertyChanged;
void SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (!object.Equals(storage, value))
{
storage = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
and
<Page.DataContext>
<local:ViewModel />
</Page.DataContext>
<Page.BottomAppBar>
<AppBar>
<Grid>
<Button HorizontalAlignment="Left" Click="Button_Click_1">Previous</Button>
<Button HorizontalAlignment="Right" Click="Button_Click_2">Next</Button>
</Grid>
</AppBar>
</Page.BottomAppBar>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<FlipView x:Name="MyFlip"
ItemsSource="{Binding ThingList}"
SelectedItem="{Binding SelectedThing,
Mode=TwoWay}" />
</Grid>
Well, it worked correctly. Could you double check? Or provide more info? Otherwise, maybe an update to your implementation using mine as a guide can solve this for you. I hope so.
In all reality I would have used a DelegateCommand to handle the buttons. But I didn't want to overdo the example with too much boiler plate code. If you want to see what I would have done, you can look here: http://blog.jerrynixon.com/2012/08/most-people-are-doing-mvvm-all-wrong.html

WPF ComboBox Binding works if previous record had no matching item in the ItemsSource collection and fails if it did

I have the following WPF Combobox:
<Window.Resources>
<CollectionViewSource x:Key="performanceItemsource" Source="{Binding Path=SelectedReport.Performances}" >
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Name"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
...
<ComboBox Name="cbxPlanPerf" Grid.ColumnSpan="2"
SelectedValuePath="MSDPortfolioID" DisplayMemberPath="Name"
SelectedValue="{Binding Path=PlanPerfID}"
ItemsSource="{Binding Source={StaticResource performanceItemsource}}"/>
The Source for the CollectionViewSource is:
public List<MSDExportProxy> Performances
{
get
{
if (Portfolio != null)
{
return (from a in Portfolio.Accounts where a.MSDPortfolioID != null select new MSDExportProxy(a))
.Concat<MSDExportProxy>(from g in Portfolio.Groups where g.MSDPortfolioID != null select new MSDExportProxy(g))
.Concat<MSDExportProxy>(from p in new[] { Portfolio } where p.MSDPortfolioID != null select new MSDExportProxy(p))
.ToList<MSDExportProxy>();
}
return new List<MSDExportProxy>();
}
}
The bound property PlanPerfID is a string.
I move between records using a ListBox control. The ComboBox works fine if the previous record had no items in its ComboBox.ItemsSource. If there were any items in the previous record's ComboBox.ItemsSource then the new record won't find its matching item in the ItemsSource collection. I've tried setting the ItemsSource in both XAML and the code-behind, but nothing changes this odd behavior. How can I get this darn thing to work?
Try using ICollectionViews in combination with IsSynchronizedWithCurrentItem property when handling lists / ObservableCollection in Xaml. The ICollectionView in the viewmodel can handle all the things needed, e.g. sorting, filtering, keeping track of selections and states.
Xaml:
<Window x:Class="ComboBoxBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0"
ItemsSource="{Binding Reports}"
DisplayMemberPath="Name"
IsSynchronizedWithCurrentItem="True" />
<ComboBox Grid.Column="1"
ItemsSource="{Binding CurrentReport.Performances}"
DisplayMemberPath="Name"
IsSynchronizedWithCurrentItem="True" />
</Grid>
</Window>
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private readonly IReportService _reportService;
private ObservableCollection<ReportViewModel> _reports = new ObservableCollection<ReportViewModel>();
private PerformanceViewModel _currentPerformance;
private ReportViewModel _currentReport;
public ObservableCollection<ReportViewModel> Reports
{
get { return _reports; }
set { _reports = value; OnPropertyChanged("Reports");}
}
public ReportViewModel CurrentReport
{
get { return _currentReport; }
set { _currentReport = value; OnPropertyChanged("CurrentReport");}
}
public PerformanceViewModel CurrentPerformance
{
get { return _currentPerformance; }
set { _currentPerformance = value; OnPropertyChanged("CurrentPerformance");}
}
public ICollectionView ReportsView { get; private set; }
public ICollectionView PerformancesView { get; private set; }
public ViewModel(IReportService reportService)
{
if (reportService == null) throw new ArgumentNullException("reportService");
_reportService = reportService;
var reports = _reportService.GetData();
Reports = new ObservableCollection<ReportViewModel>(reports);
ReportsView = CollectionViewSource.GetDefaultView(Reports);
ReportsView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
ReportsView.CurrentChanged += OnReportsChanged;
ReportsView.MoveCurrentToFirst();
}
private void OnReportsChanged(object sender, EventArgs e)
{
var selectedReport = ReportsView.CurrentItem as ReportViewModel;
if (selectedReport == null) return;
CurrentReport = selectedReport;
if(PerformancesView != null)
{
PerformancesView.CurrentChanged -= OnPerformancesChanged;
}
PerformancesView = CollectionViewSource.GetDefaultView(CurrentReport.Performances);
PerformancesView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
PerformancesView.CurrentChanged += OnPerformancesChanged;
PerformancesView.MoveCurrentToFirst();
}
private void OnPerformancesChanged(object sender, EventArgs e)
{
var selectedperformance = PerformancesView.CurrentItem as PerformanceViewModel;
if (selectedperformance == null) return;
CurrentPerformance = selectedperformance;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I found a quick and dirty solution to my problem. I just happen to have a public NotifyPropertyChanged() method on my Report entity and I discovered that if I called SelectedReport.NotifyPropertyChanged("PlanPerfID") in the Report ListBox's SelectionChanged event that it was enough of a jolt to get the ComboBox to re-evaluate and find its matching item in the ItemsSource. Yeah, it's KLUGE...
UPDATE: I also wound up needing to add SelectedReport.NotifyPropertyChanged("Performances") for some situations...
UPDATE 2: Okay, turns out the above wasn't bullet proof and I ran across a situation that broke it so I had to come up with a better workaround:
Altered the SelectedReport property in the Window's code-behind, adding a private flag (_settingCombos) to keep the Binding from screwing up the bound values until the dust has settled from changin the ItemSource:
private bool _settingCombos = false;
private Report _SelectedReport;
public Report SelectedReport
{
get { return _SelectedReport; }
set
{
_settingCombos = true;
_SelectedReport = value;
NotifyPropertyChanged("SelectedReport");
}
}
Created a proxy to bind to in the Window code-behind that will refuse to update the property's value if the _settingCombos flag is true:
public string PlanPerfID_Proxy
{
get { return SelectedReport.PlanPerfID; }
set
{
if (!_settingCombos)
{
SelectedReport.PlanPerfID = value;
NotifyPropertyChanged("PlanPerfID_Proxy");
}
}
}
Added an extra Notification in the Report ListBox's SelectionChanged event along with code to reset the _settingCombos flag back to false:
private void lbxReports_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//KLUGE: Couldn't get the ComboBoxes associated with these properties to work right
//this forces them to re-evaluate after the Report has loaded
if (SelectedReport != null)
{
NotifyPropertyChanged("PlanPerfID_Proxy");
_settingCombos = false;
}
}
Bound the ComboBox to the PlanPerfID_Proxy property (instead of directly to the SelectedReport.PlanPerfID property.
Wow, what a hassle! I think that this is simply a case of .NET's binding logic getting confused by the dynamic nature of the ComboBox.ItemSource, but this seems to have fixed it. Hope it helps someone else.

Why is my WPF CheckBox Binding not working?

I'm using MVVM, VS 2008, and .NET 3.5 SP1. I have a list of items, each exposing an IsSelected property. I have added a CheckBox to manage the selection/de-selection of all the items in the list (updating each item's IsSelected property). Everything is working except the IsChecked property is not being updated in the view when the PropertyChanged event fires for the CheckBox's bound control.
<CheckBox
Command="{Binding SelectAllCommand}"
IsChecked="{Binding Path=AreAllSelected, Mode=OneWay}"
Content="Select/deselect all identified duplicates"
IsThreeState="True" />
My VM:
public class MainViewModel : BaseViewModel
{
public MainViewModel(ListViewModel listVM)
{
ListVM = listVM;
ListVM.PropertyChanged += OnListVmChanged;
}
public ListViewModel ListVM { get; private set; }
public ICommand SelectAllCommand { get { return ListVM.SelectAllCommand; } }
public bool? AreAllSelected
{
get
{
if (ListVM == null)
return false;
return ListVM.AreAllSelected;
}
}
private void OnListVmChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "AreAllSelected")
OnPropertyChanged("AreAllSelected");
}
}
I'm not showing the implementation of SelectAllCommand or individual item selection here, but it doesn't seem to be relevant. When the user selects a single item in the list (or clicks the problem CheckBox to select/de-select all items), I have verified that the OnPropertyChanged("AreAllSelected") line of code executes, and tracing in the debugger, can see the PropertyChanged event is subscribed to and does fire as expected. But the AreAllSelected property's get is only executed once - when the view is actually rendered. Visual Studio's Output window does not report any data binding errors, so from what I can tell, the CheckBox's IsSelected property is properly bound.
If I replace the CheckBox with a Button:
<Button Content="{Binding SelectAllText}" Command="{Binding SelectAllCommand}"/>
and update the VM:
...
public string SelectAllText
{
get
{
var msg = "Select All";
if (ListVM != null && ListVM.AreAllSelected != null && ListVM.AreAllSelected.Value)
msg = "Deselect All";
return msg;
}
}
...
private void OnListVmChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "AreAllSelected")
OnPropertyChanged("SelectAllText");
}
everything works as expected - the button's text is updated as all items are selected/desected. Is there something I'm missing about the Binding on the CheckBox's IsSelected property?
Thanks for any help!
I found the problem. It seems a bug existed in WPF 3.0 with OneWay bindings on IsChecked causing the binding to be removed. Thanks to this post for the assistance, it sounds like the bug was fixed in WPF 4.0
To reproduce, create a new WPF project.
Add a FooViewModel.cs:
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace Foo
{
public class FooViewModel : INotifyPropertyChanged
{
private bool? _isCheckedState = true;
public FooViewModel()
{
ChangeStateCommand = new MyCmd(ChangeState);
}
public bool? IsCheckedState
{
get { return _isCheckedState; }
}
public ICommand ChangeStateCommand { get; private set; }
private void ChangeState()
{
switch (_isCheckedState)
{
case null:
_isCheckedState = true;
break;
default:
_isCheckedState = null;
break;
}
OnPropertyChanged("IsCheckedState");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
changed(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyCmd : ICommand
{
private readonly Action _execute;
public event EventHandler CanExecuteChanged;
public MyCmd(Action execute)
{
_execute = execute;
}
public void Execute(object parameter)
{
_execute();
}
public bool CanExecute(object parameter)
{
return true;
}
}
}
Modify Window1.xaml.cs:
using System.Windows;
using System.Windows.Controls.Primitives;
namespace Foo
{
public partial class Window1
{
public Window1()
{
InitializeComponent();
}
private void OnClick(object sender, RoutedEventArgs e)
{
var bindingExpression = MyCheckBox.GetBindingExpression(ToggleButton.IsCheckedProperty);
if (bindingExpression == null)
MessageBox.Show("IsChecked property is not bound!");
}
}
}
Modify Window1.xaml:
<Window
x:Class="Foo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Foo"
Title="Window1"
Height="200"
Width="200"
>
<Window.DataContext>
<vm:FooViewModel />
</Window.DataContext>
<StackPanel>
<CheckBox
x:Name="MyCheckBox"
Command="{Binding ChangeStateCommand}"
IsChecked="{Binding Path=IsCheckedState, Mode=OneWay}"
Content="Foo"
IsThreeState="True"
Click="OnClick"/>
<Button Command="{Binding ChangeStateCommand}" Click="OnClick" Content="Change State"/>
</StackPanel>
</Window>
Click on the button a few times and see the CheckBox's state toggle between true and null (not false). But click on the CheckBox and you will see that the Binding is removed from the IsChecked property.
The workaround:
Update the IsChecked binding to be TwoWay and set its UpdateSourceTrigger to be explicit:
IsChecked="{Binding Path=IsCheckedState, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
and update the bound property so it's no longer read-only:
public bool? IsCheckedState
{
get { return _isCheckedState; }
set { }
}

Categories

Resources