I have created a simple attached property that enables dragging an item around the screen.
1/ Here's how you would implement it on your element:
<Rectangle Fill="Green" local:MyExtension.CanMove="True" />
2/ This works like a charm. So does this:
// in resources
<x:Boolean x:Key="MyCanMove">true</x:Boolean>
<Rectangle Fill="Blue" local:MyExtension.CanMove="{StaticResource MyCanMove}" />
3/ But one syntax does not work. This fails:
<Rectangle Fill="Red" local:MyExtension.CanMove="{Binding Path=CanMove}" />
What's different? The only thing different is that it is binding the value into the attached property instead of setting it explicitly or through a static resource.
I'm missing something. But what is it?
Here's the full XAML:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.DataContext>
<local:ViewModel/>
</Grid.DataContext>
<ToggleSwitch Header="Enable Dragging"
HorizontalAlignment="Center"
IsOn="{Binding CanMove, Mode=TwoWay}">
<ToggleSwitch.RenderTransform>
<TranslateTransform Y="-100" />
</ToggleSwitch.RenderTransform>
</ToggleSwitch>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel.Resources>
<Style TargetType="Rectangle">
<Setter Property="Height" Value="100" />
<Setter Property="Width" Value="100" />
</Style>
<x:Boolean x:Key="MyCanMove">true</x:Boolean>
</StackPanel.Resources>
<Rectangle Fill="Green" local:MyExtension.CanMove="True" />
<Rectangle Fill="Blue" local:MyExtension.CanMove="{StaticResource MyCanMove}" />
<Rectangle Fill="Red" local:MyExtension.CanMove="{Binding Path=CanMove}" />
</StackPanel>
</Grid>
And here's the full code-behind:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
bool m_CanMove = true;
public bool CanMove
{
get { return m_CanMove; }
set
{
m_CanMove = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("CanMove"));
}
}
}
public class MyExtension
{
// canmove aproperty
public static bool GetCanMove(DependencyObject obj)
{
return (bool)obj.GetValue(CanMoveProperty);
}
public static void SetCanMove(DependencyObject obj, bool value)
{
System.Diagnostics.Debug.WriteLine("SetCanMove");
obj.SetValue(CanMoveProperty, value);
var rectangle = obj as FrameworkElement;
rectangle.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY;
rectangle.ManipulationDelta -= rectangle_ManipulationDelta;
if (value)
rectangle.ManipulationDelta += rectangle_ManipulationDelta;
}
public static readonly DependencyProperty CanMoveProperty =
DependencyProperty.RegisterAttached("CanMove", typeof(bool), typeof(MyExtension), new PropertyMetadata(false));
// implementation
static void rectangle_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var rectangle = sender as FrameworkElement;
var canMove = System.Convert.ToBoolean(rectangle.GetValue(MyExtension.CanMoveProperty));
if (canMove)
{
var transform = rectangle.RenderTransform as CompositeTransform;
if (transform == null)
rectangle.RenderTransform = (transform = new CompositeTransform());
transform.TranslateX += e.Delta.Translation.X;
transform.TranslateY += e.Delta.Translation.Y;
}
}
}
I'll remind you that this attached property works fine in the first two syntaxes. As a result, I can't imagine the error is in the attached property. And, I read on a few forums where path= is necessary to bind to an attached property, so I included that (though it didn't make a difference). Changing the Mode (OneWay, TwoWay) doesn't make a difference. Binding with an ElementName didn't make a difference. I am wondering if this simply isn't enabled in Windows 8.0 WinRT. Can anyone else get this to work?
EDIT: Solution
The problem was that without a changed event handler setup, the binding doesn't raise a changed event. Here's the updated MyExtension code:
public class MyExtension
{
// canmove aproperty
public static bool GetCanMove(DependencyObject obj) { return (bool)obj.GetValue(CanMoveProperty); }
public static void SetCanMove(DependencyObject obj, bool value) { obj.SetValue(CanMoveProperty, value); }
public static readonly DependencyProperty CanMoveProperty =
DependencyProperty.RegisterAttached("CanMove", typeof(bool), typeof(MyExtension), new PropertyMetadata(false, OnCanMoveChanged));
// respond to change
private static void OnCanMoveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var rectangle = d as FrameworkElement;
rectangle.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY;
rectangle.ManipulationDelta -= rectangle_ManipulationDelta;
if ((bool)e.NewValue)
rectangle.ManipulationDelta += rectangle_ManipulationDelta;
}
// implementation
static void rectangle_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var rectangle = sender as FrameworkElement;
var canMove = System.Convert.ToBoolean(rectangle.GetValue(MyExtension.CanMoveProperty));
if (canMove)
{
var transform = rectangle.RenderTransform as CompositeTransform;
if (transform == null)
rectangle.RenderTransform = (transform = new CompositeTransform());
transform.TranslateX += e.Delta.Translation.X;
transform.TranslateY += e.Delta.Translation.Y;
}
}
}
This is just speculation as I don't have a compiler in front of me, but I'm wondering if the binding infrastructure doesn't use the exposed methods you created GetCanMove etc.
Try registering a property changed method in the PropertyMetadata
new PropertyMetadata(false, OnCanMoveChanged)
and have the setup and teardown code in there
private void OnCanMoveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
Related
I have an entry control in my XAML where I set the initial value on page appear through data binding. Initially the value is appearing but when I am updating it from another view model it is not getting updated on UI.
Below is the XAML code and XAML.CS
<ListView
x:Name="workList"
Grid.Row="2"
SeparatorColor="{DynamicResource AccentColor}"
ItemsSource="{ Binding WorkItems }"
Margin="5"
CachingStrategy="RecycleElement"
RowHeight="440"
SeparatorVisibility="Default"
SelectionMode="None"
HasUnevenRows="False">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<local:LoadItemPutawayTemplate />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
mc:Ignorable="d"
x:Class="Sanipex.LoadItemPutawayTemplate">
<Grid
RowSpacing="0"
Padding="0"
Margin="0,10,0,0"
>
<Grid.RowDefinitions>
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<Entry
x:Name="OverrideLoc"
Grid.Row="0"
TextColor="Black"
WidthRequest="110"
Text="{Binding toLocation}"
grial:EntryProperties.BorderCornerRadius="10"
grial:EntryProperties.BorderStyle="RoundRect"
grial:EntryProperties.BorderColor="Black"
HorizontalOptions="StartAndExpand"
VerticalOptions="Center"
Focused="OverrideLoc_Focused"
TextChanged="OverrideLoc_TextChanged"
grial:EntryProperties.HorizontalPadding="5"
FontAttributes="Bold"
PlaceholderColor="Black"
FontSize="20"/>
</Grid>
public partial class ItemPutAway : ContentPage
{
private static ItemPutAwayViewModel obj;
public ItemPutAway()
{
InitializeComponent();
obj = new ItemPutAwayViewModel();
BindingContext = obj;
}
public static ItemPutAwayViewModel itemPutAwayViewModel
{
get
{
return obj;
}
}
protected override async void OnAppearing()
{
obj.LoadData();
}
}
Below is my first view model code
public class ItemPutAwayViewModel : INotifyPropertyChanged
{
private IList<WorkItem> workItems;
public event PropertyChangedEventHandler PropertyChanged;
public string ltoLocation;
public string toLocation
{
get => ltoLocation;
set
{
ltoLocation = value;
OnPropertyChanged(nameof(toLocation));
}
}
public IList<WorkItem> WorkItems
{
get => workItems;
set
{
workItems = value;
OnPropertyChanged(nameof(WorkItems));
}
}
public void LoadData()
{
WorkItems = App.dataManager.GetItemPutAwayWorks();
}
public void setLocation(string _location)
{
toLocation = _location;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Below is the code through which I am trying to update the toLocation binding value to different value from another XAML page as below:
public partial class AvailableLocationsPopUp : PopupPage
{
private static AvailableLocationViewModel obj;
public AvailableLocationsPopUp(WorkItem _workItem)
{
InitializeComponent();
obj = new AvailableLocationViewModel(gWorkItem.itemid);
BindingContext = obj;
}
private void OnClose(object sender, EventArgs e)
{
PopupNavigation.Instance.PopAsync();
}
private void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
Location content = e.Item as Location;
ItemPutAway.itemPutAwayViewModel.setLocation("ABC-XYZ");
PopupNavigation.Instance.PopAsync();
}
}
As I mentioned in the discussion, you have to also implement the INotifyPropertyChanged interface of the class WorkItem.
Implement INotifyPropertyChanged in ItemPutAwayViewModel will only help for changes in the WorkItems(like add or remove one WorkItem), not the changes inside the WorkItem.
So, the code should be:
public class WorkItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _toLocation;
public string toLocation
{
get => _toLocation;
set
{
_toLocation = value;
NotifyPropertyChanged();
}
}
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I have a view that uses the SearchBox user control, The SearchBox has two radio buttons to select the search modes - Instant and delayed. I have binded the searchmodes to SearchMode property, and also I have created a custom dependency property for the Search Mode.
View
<controls:SearchBox Grid.Row="0"
HorizontalAlignment="Right"
Margin="2" Width="200"
SearchMode="{Binding DataContext.SearchMode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }" />
ViewModel.cs
private Mode mSearchMode;
public Mode SearchMode
{
get
{
return mSearchMode;
}
set
{
mSearchMode = value;
NotifyOfPropertyChange();
}
}
// Called when application is restarted.
private void ActivateLastSelectedSearchMode(Mode lastselectedMode)
{
// Sets the last selected mode to the search mode
SearchMode = lastselectedMode;
}
public enum Mode
{
Instant,
Delayed,
}
SearchBox.xaml
<UserControl x:Class = "abc.SearchBox"
DataContext="{Binding RelativeSource={RelativeSource Self}}" >
<UserControl.Resources>
<converters:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<RadioButton Content="{lex:Loc SearchBox:SearchModelInstatOption}"
IsChecked="{Binding Path=SearchMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:Mode.Instant}}" />
<RadioButton Content="{lex:Loc SearchBox:SearchModeDelayedOption}"
IsChecked="{Binding Path=SearchMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:Mode.Delayed}}" />
</StackPanel>
</UserControl>
SearchBox.xaml.cs
public partial class SearchBox : UserControl
{
public static DependencyProperty SearchModeProperty =
DependencyProperty.Register(
"SearchMode",
typeof(Mode),
typeof(SearchBox),
new FrameworkPropertyMetadata(default(Mode), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsSearchModeChanged));
static void OnIsSearchModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var searchBox = obj as SearchBox;
searchBox.SearchMode = (Mode)e.NewValue;
}
public Mode SearchMode
{
get { return (Mode)GetValue(SearchModeProperty); }
set { SetValue(SearchModeProperty, value); }
}
}
I want the OnIsSearchModeChanged() to be fired each time when SearchMode is set during call back i e, ActivateLastSelectedSearchMode() is invoked in ViewModel.cs. I am absolutely clueless..where I am missing, I am unable to achieve success.
//snip
private Mode mSearchMode;
public Mode SearchMode
{
get
{
return mSearchMode;
}
set
{
mSearchMode = value;
NotifyOfPropertyChange(()=>SearchMode); //Change
}
}
does the reflected change make any difference? Other option would be to create a custom convention for your user control
You should create an Event in you View Model and subscribe to it from your code behind.
In your View Model :
public event SearchModeAction SearchModeChanged;
public delegate void SearchModeAction(object sender, EventArgs e);
public void SearchModeHasChanged()
{
SearchModeAction Handler = SearchModeChanged;
if (Handler != null)
{
Handler(this, null);
}
}
private void ActivateLastSelectedSearchMode(Mode lastselectedMode)
{
// Sets the last selected mode to the search mode
SearchMode = lastselectedMode;
SearchModeHasChanged()
}
In your Code Behind :
private void Window_Loaded(object sender, RoutedEventArgs e)
{
((YourViewModelClass)DataContext).SearchModeChanged += OnIsSearchModeChanged;
}
private void OnIsSearchModeChanged(object sender, EventArgs e)
{
var searchBox = obj as SearchBox;
searchBox.SearchMode = (Mode)e.NewValue;
}
This way each time you arrive in your ActivateLastSelectedSearchMode method in your View Model, you will call your OnIsSearchModeChanged method in your View.
Ahh..the reason was the EnumToBooleanConverter.
Though the value of 'parameter' and 'value' was same, There was a difference between their object types as both were referencing to different namespaces. So I created a public enum called 'Mode' and ensured that the 'Instant' and 'Delayed' reference to the same namespace.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
{
return false;
}
return value.Equals(parameter); // This always returned false despite the values being the same
}
When using touch to trigger a drag and drop action for a ListView item, it would appear that the behavior has changed between WinRT (Windows 8/8.1) and UWP (Windows 10) apps.
In WinRT, "tearing" an item to the left or right would cause it to get detached, initiating the drag behavior. In UWP, the user has to tap and hold an item for a short while, after which moving it initiates the drag action.
My question is: is there a way to revert to/implement the old WinRT-style behavior? The new way is not very obvious and in limited user testing I haven't seen one person work it out without having it explained to them.
As a quick example, the following XAML works for both WinRT and UWP, however the touch-based interactions are much discoverable in WinRT.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView AllowDrop="True" CanReorderItems="True">
<ListView.Items>
<x:String>Item 1</x:String>
<x:String>Item 2</x:String>
<x:String>Item 3</x:String>
<x:String>Item 4</x:String>
<x:String>Item 5</x:String>
</ListView.Items>
</ListView>
</Grid>
I wanted similar behaviour that was questioned here as I was annoyed with the default win10 behaviour. There might be better solutions, but this one is what I came up.
<GridView Name="MySourceGridView" ItemsSource="{x:Bind Animals}" ScrollViewer.VerticalScrollMode="Disabled" >
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Animal">
<StackPanel Margin="20" Width="200" Height="200" PointerPressed="StackPanel_PointerPressed" DragStarting="StackPanel_DragStarting">
<StackPanel.Background>
<SolidColorBrush Color="{x:Bind Color}" />
</StackPanel.Background>
<TextBlock Text="{x:Bind Name}" />
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Above is my GridView and binded data. I noticed that if I use gridviews candrop and dragstarted events, I just couldn't do what I liked. So I used datatemplates stackpanels pointerpressed, which launches immediatly on touch.
private async void StackPanel_PointerPressed(object sender, PointerRoutedEventArgs e)
{
var obj = (StackPanel)sender;
if (obj != null)
{
var pointerPoint = e.GetCurrentPoint(sender as UIElement);
await obj.StartDragAsync(pointerPoint);
}
}
There you have the pointerPoint too. The dragstarted on the stackpanel to move the data.
private void StackPanel_DragStarting(UIElement sender, DragStartingEventArgs args)
{
var senderElement = sender as FrameworkElement;
var ani = (Animal)senderElement.DataContext;
args.Data.SetText(ani.ID.ToString());
args.Data.RequestedOperation = DataPackageOperation.Copy;
}
Rest is just the normal data catching after I managed to pass the ID of the Animal in my list of animals forward.
private void MyTargetRectangle_DragEnter(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Copy;
e.DragUIOverride.Caption = "Kokeiles";
e.DragUIOverride.IsCaptionVisible = true;
e.DragUIOverride.IsContentVisible = true;
e.DragUIOverride.IsGlyphVisible = false;
}
private async void MyTargetRectangle_Drop(object sender, DragEventArgs e)
{
var droppedAnimalId = await e.DataView.GetTextAsync();
Animal ani = Animals.Where(p => p.ID == int.Parse(droppedAnimalId)).FirstOrDefault();
MyTargetRectangle.Fill = new SolidColorBrush(ani.Color);
}
I hope this helps someone and is not too long answer.
I've finally figured out how to get back the old Windows 8.1 behavior for the ListView. It still allows Scrolling with touch and starts a drag operation of one item if you swipe perpendicularly to the scroll axis. It's based on the Comet library and is implemented by a custom ListView. The idea is to allow TranslateX/TranslateY and System Manipulations in the ListViewItem. For this you need to override the default ListViewItem's style.
If you want to use the control, you have to bear in mind a few things:
Copy the styles in Themes/Generic.xaml and adapt the local2 Namespace.
If you use a horizontally scrolling ListView, you must set the Orientation property of the ListView accordingly. The control doesn't detect the used ItemsPanel.
You can still use the regular UWP drag & drop mechanism, but you must subscribe to a second Event called ItemStartDragging for the old Windows 8.1 style dragging.
If you handle the Drop event when using the 8.1 style dragging, the data can be found in DragEventArgs.DataView, whereas it could be found in DragEventArgs.Data.GetView() when using DragItemStarting (=default event). Don't know why they behave differently.
The styles are very basic. You might want to change them and make them more akin to the original ListViewItem styles.
Here's the code:
public class DraggingListView : ListView
{
public DraggingListView()
{
}
protected override DependencyObject GetContainerForItemOverride()
{
if (Orientation == Orientation.Horizontal)
return new HorizontalDraggingListItem(this);
else
return new VerticalDraggingListItem(this);
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
(element as DraggingListItem).DataContext = item;
(element as DraggingListItem).MouseSlidingEnabled = MouseSlidingEnabled;
}
public event EventHandler<ListItemStartDraggingEventArgs> ItemStartDragging;
public void OnChildItemDragged(DraggingListItem item, Windows.ApplicationModel.DataTransfer.DataPackage data)
{
if (ItemStartDragging == null)
return;
ItemStartDragging(this, new ListItemStartDraggingEventArgs(data, item.DataContext));
}
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(DraggingListView), new PropertyMetadata(Orientation.Vertical));
/// <summary>
/// Gets or sets the ability to slide the control with the mouse. False by default
/// </summary>
public bool MouseSlidingEnabled
{
get { return (bool)GetValue(MouseSlidingEnabledProperty); }
set { SetValue(MouseSlidingEnabledProperty, value); }
}
public static readonly DependencyProperty MouseSlidingEnabledProperty =
DependencyProperty.Register("MouseSlidingEnabled", typeof(bool), typeof(DraggingListView), new PropertyMetadata(false));
}
public class ListItemStartDraggingEventArgs : EventArgs
{
public Windows.ApplicationModel.DataTransfer.DataPackage Data { get; private set; }
public object Item { get; private set; }
public ListItemStartDraggingEventArgs(Windows.ApplicationModel.DataTransfer.DataPackage data, object item)
{
Data = data;
Item = item;
}
}
public class HorizontalDraggingListItem : DraggingListItem
{
public HorizontalDraggingListItem(DraggingListView listView) : base(listView)
{
this.DefaultStyleKey = typeof(HorizontalDraggingListItem);
}
protected override bool DetectDrag(ManipulationDelta delta)
{
return Math.Abs(delta.Translation.Y) > 2;
}
}
public class VerticalDraggingListItem : DraggingListItem
{
public VerticalDraggingListItem(DraggingListView listView) : base(listView)
{
this.DefaultStyleKey = typeof(VerticalDraggingListItem);
}
protected override bool DetectDrag(ManipulationDelta delta)
{
return Math.Abs(delta.Translation.X) > 2;
}
}
[TemplatePart(Name = PART_CONTENT_GRID, Type = typeof(Grid))]
public abstract class DraggingListItem : ListViewItem
{
const string PART_CONTENT_GRID = "ContentGrid";
private Grid contentGrid;
private DraggingListView _listView;
public DraggingListItem(DraggingListView listView)
{
_listView = listView;
this.DragStarting += OnDragStarting;
}
private void OnDragStarting(UIElement sender, DragStartingEventArgs args)
{
_listView.OnChildItemDragged(this, args.Data);
}
protected override void OnApplyTemplate()
{
contentGrid = this.GetTemplateChild(PART_CONTENT_GRID) as Grid;
contentGrid.ManipulationDelta += ContentGrid_ManipulationDelta;
contentGrid.ManipulationCompleted += ContentGrid_ManipulationCompleted;
contentGrid.PointerPressed += ContentGrid_PointerPressed;
base.OnApplyTemplate();
}
private PointerPoint pp = null;
private void ContentGrid_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (!MouseSlidingEnabled && e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
return;
pp = e.GetCurrentPoint(sender as UIElement);
}
private void ContentGrid_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
{
if (!MouseSlidingEnabled && e.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
return;
pp = null;
}
private async void ContentGrid_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
if (!MouseSlidingEnabled && e.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
return;
if (DetectDrag(e.Delta) && pp != null)
{
var pointer = pp;
pp = null;
await StartDragAsync(pointer);
}
}
protected abstract bool DetectDrag(ManipulationDelta delta);
#region Dependency Properties
/// <summary>
/// Gets or sets the ability to slide the control with the mouse. False by default
/// </summary>
public bool MouseSlidingEnabled
{
get { return (bool)GetValue(MouseSlidingEnabledProperty); }
set { SetValue(MouseSlidingEnabledProperty, value); }
}
public static readonly DependencyProperty MouseSlidingEnabledProperty =
DependencyProperty.Register("MouseSlidingEnabled", typeof(bool), typeof(DraggingListItem), new PropertyMetadata(false));
#endregion
}
And this is the XAML in Generic.xaml:
<Style TargetType="local2:HorizontalDraggingListItem" >
<Setter Property="VerticalAlignment" Value="Stretch"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:HorizontalDraggingListItem">
<Grid ManipulationMode="TranslateY,System" x:Name="ContentGrid" Background="{TemplateBinding Background}">
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="local2:VerticalDraggingListItem" >
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local2:VerticalDraggingListItem">
<Grid ManipulationMode="TranslateX,System" x:Name="ContentGrid" Background="{TemplateBinding Background}">
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In WinRT, "tearing" an item to the left or right would cause it to get detached, initiating the drag behavior. In UWP, the user has to tap and hold an item for a short while, after which moving it initiates the drag action.
Yes, the Behavior to start Drag action has been changed in UWP app.
My question is: is there a way to revert to/implement the old WinRT-style behavior?
The possible way is to create a Drag Behavior and attach to ListView, in this Behavior, we can handle the related touch event and use UIElement.StartDragAsync method to initiates a drag-and-drop operation programmatically, to find the current ListViewItem,
public class FrameworkElementDragBehavior : DependencyObject, IBehavior
{
private bool isMouseClicked = false;
public DependencyObject AssociatedObject { get; private set; }
public void Attach(DependencyObject associatedObject)
{
var control = associatedObject as Control;
if (control == null)
throw new ArgumentException(
"FrameworkElementDragBehavior can be attached only to Control");
AssociatedObject = associatedObject;
((FrameworkElement)this.AssociatedObject).Holding += FrameworkElementDragBehavior_Holding;
((FrameworkElement)this.AssociatedObject).DragStarting += FrameworkElementDragBehavior_DragStarting;
}
private void FrameworkElementDragBehavior_Holding(object sender, Windows.UI.Xaml.Input.HoldingRoutedEventArgs e)
{
//Just for example, not the completed code
var obj = ((ListView)sender).SelectedItem as ListViewItem;
if (obj != null)
{
//Call the UIElement.StartDragAsync method
}
}
private void FrameworkElementDragBehavior_DragStarting(UIElement sender, DragStartingEventArgs args)
{
throw new NotImplementedException();
}
public void Detach()
{
AssociatedObject = null;
}
}
I believe this is possible, but I don't seem to be able to get it to work; here's my view model:
namespace MyApp.ViewModel
{
public class MainViewModel : INotifyPropertyChanged
{
private static MainViewModel _mvm;
public static MainViewModel MVM()
{
if (_mvm == null)
_mvm = new MainViewModel();
return _mvm;
}
private string _imagePath = #"c:\location\image.png";
public string ImagePath
{
get { return _imagePath; }
set
{
SetProperty<string>(ref _imagePath, value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged<T>(propertyName);
return true;
}
private void OnPropertyChanged<T>([CallerMemberName]string caller = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(caller));
}
}
...
Here's my App.xaml:
xmlns:vm="using:MyApp.ViewModel">
<Application.Resources>
<ResourceDictionary>
<vm:MainViewModel x:Key="MainViewModel" />
</ResourceDictionary>
</Application.Resources>
Here's the binding:
<Page
...
DataContext="{Binding MVM, Source={StaticResource MainViewModel}}">
<StackPanel Orientation="Horizontal" Margin="20" Grid.Row="0">
<TextBlock FontSize="30" Margin="10">Image</TextBlock>
<TextBox Text="{Binding ImagePath}" Margin="10"/>
</StackPanel>
...
I don't seem to be able to get the binding to work; what have I missed here? I would expect the field to be populated with the default value, but it isn't; I've put breakpoints in the ViewModel, but it is not breaking.
To me your binding syntax is incorrect. DataContext="{Binding MVM, Source={StaticResource MainViewModel} means you should have a "MVM" PROPERTY in your MainViewModel class. In your case MVM is a method.
Try replacing your MVM method by a property. That might work.
Another way to do it, is to set
DataContext="{StaticResource MainViewModel}"
In that case, the MVM method will be obsolete (I did not try it on WinRT)
I need to bind all the check boxes(IsChecked Property) in an StackPannel with a bool value which is defined in a class. Attaching my Xaml with my question, please help
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Height="287" HorizontalAlignment="Left" Margin="78,65,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="309" DataContext="checkFlag">
<CheckBox Content="" Height="71" Name="checkBox1" IsChecked="{Binding Path=Binding.MainPage,Source=checkFlag,Mode=TwoWay}"/>
<CheckBox Content="" Height="71" Name="checkBox2" IsChecked="{Binding Path=Binding.MainPage,Source=checkFlag,Mode=TwoWay}"/>
</StackPanel>
<Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="78,400,0,0" Name="button1" VerticalAlignment="Top" Width="160" Click="button1_Click" />
<Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="227,400,0,0" Name="button2" VerticalAlignment="Top" Width="160" Click="button2_Click" />
The checkboxes are not getting check/uncheck on set/reset of checkFlag. Should i implement "INotifyPropertyChanged" for the flag Or something else. Adding the class behind also, Please have a look.'
namespace Binding
{
public partial class MainPage : PhoneApplicationPage
{
public bool checkFlag;
// Constructor
public MainPage()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
checkFlag = true;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
checkFlag = false;
}
}
it looks to me like you need to implement, as you guess, INotifyPropertyChanged here is a link to the MSDN documentation
here is some demo code....
public class MyData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
PropertyChangedEventArgs args = new PropertyChangedEventArgs(property);
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, args);
}
}
int _someInt = 0;
public int SomeInt
{
get { return _someInt ;}
set { if ( value == _someInt ) return;
_someInt = value;
RaisePropertyChanged("SomeInt");
}
}
string _someString = string.Empty;
public string SomeString
{
get { return _someString ;}
set { if ( value == _someString ) return;
_someString = value;
RaisePropertyChanged("SomeString ");
}
}
}
you set up your private variables, then add a property for each one you want to expose.
in the getter, just return the value... in the setter, check if the value has changed and if so assign the value and raise the property changed event. make sure that the name you are passing in as a string is the same as the property... same capitalization, same spelling, etc.