UWPでカスタム数値ピッカーコントロールを使用していて、ビューモデルをSelectedValueプロパティにバインドしようとしています。現在のところ、双方向バインディングと更新トリガーがPropertyChanged
に設定されていても、私のバインドはいずれの方向にも機能しません。今はイベントハンドラを使って作業してきましたが、私はこのコントロールを会社のカスタムコントロール用のライブラリに分けて、すぐに使用できるようにしたいと考えています。私の制御コードと私はコントロールを使用しているページの基本的なコードは以下の通りです:UWPカスタムコントロールバインディングがどちらの方向にも動作しません
NumberPicker.xaml:
<ItemsControl
x:Class="UWPApp.Scorekeeper.NumberPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWPApp.Scorekeeper"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vms="using:UWPApp.Scorekeeper.Models.ViewModels"
mc:Ignorable="d"
x:Name="Select"
Loaded="Select_Loaded"
ItemsSource="{x:Bind ItemsCollection}"
d:DesignHeight="300"
d:DesignWidth="400">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:NumberItem">
<Viewbox HorizontalAlignment="Stretch" Height="115">
<TextBlock Text="{x:Bind Value}"></TextBlock>
</Viewbox>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate>
<Grid BorderThickness="4" BorderBrush="Black">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Rectangle Opacity=".5">
<Rectangle.Fill>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Offset="0" Color="Black"/>
<GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<ScrollViewer Grid.RowSpan="3" ViewChanged="Select_ViewChanged" VerticalSnapPointsType="Mandatory" VerticalSnapPointsAlignment="Center" x:Name="MinutesSelect" HorizontalScrollMode="Disabled" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Visible">
<ItemsPresenter></ItemsPresenter>
</ScrollViewer>
<Rectangle Grid.Row="2" Opacity=".5">
<Rectangle.Fill>
<LinearGradientBrush StartPoint=".5,1" EndPoint=".5,0">
<GradientStop Offset="0" Color="Black"/>
<GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
NumberPicker.xaml.cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using UWPApp.Scorekeeper.Models.ViewModels;
using UWPApp.Scorekeeper.Toolbox;
namespace UWPApp.Scorekeeper
{
public class NumberItem
{
public NumberItem(int? value)
{
Value = value;
}
public int? Value { get; set; }
}
public sealed partial class NumberPicker : ItemsControl
{
public event SelectionChangedEventHandler SelectionChanged;
public int RangeBottom { get; set; }
public int RangeTop { get; set; }
public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(int?), typeof(NumberPicker), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedValueChanged)));
public static readonly DependencyProperty SelectionChangedProperty = DependencyProperty.Register("SelectionChanged", typeof(SelectionChangedEventHandler), typeof(NumberPicker), new PropertyMetadata(null));
private static void OnSelectedValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var picker = d as NumberPicker;
picker.SelectionChanged?.Invoke(picker, new SelectionChangedEventArgs(new List<object> { e.OldValue }, new List<object> { e.NewValue }));
return;
}
public int? SelectedValue { get { return (int?)GetValue(SelectedValueProperty); } set { SetValue(SelectedValueProperty, value); } }
public ObservableCollection<NumberItem> ItemsCollection { get; set; }
public NumberPicker()
{
this.InitializeComponent();
DataContext = this;
ItemsCollection = new ObservableCollection<NumberItem>();
}
private void Select_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (!e.IsIntermediate)
{
var scroll = sender as ScrollViewer;
var position = scroll.VerticalOffset;
var value = Math.Floor(position/115d);
SelectedValue = ((int)value);
}
}
private void Select_Loaded(object sender, RoutedEventArgs e)
{
var count = RangeTop - RangeBottom + 1;
var items = Enumerable.Range(RangeBottom, count).Select(m => new NumberItem(m)).ToList();
foreach (var item in items)
{
ItemsCollection.Add(item);
}
ItemsCollection.Insert(0, new NumberItem(null));
ItemsCollection.Add(new NumberItem(null));
var period = TimeSpan.FromMilliseconds(10);
Windows.System.Threading.ThreadPoolTimer.CreateTimer(async (source) =>
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,() =>
{
var scroll = Select.FindFirstChild<ScrollViewer>();
if (SelectedValue != null)
{
var position = SelectedValue * 115d + 81.5;
scroll.ChangeView(null, position, null, true);
}
});
}, period);
}
}
}
ます。 XAML:
<Page
x:Class="UWPApp.Scorekeeper.SelectPenaltyTime"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWPApp.Scorekeeper"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vms="using:UWPApp.Scorekeeper.Models.ViewModels"
mc:Ignorable="d"
x:Name="PageElement"
Background="{ThemeResource SystemControlBackgroundAccentBrush}"
d:DesignHeight="600"
d:DesignWidth="1024">
<ContentPresenter x:Name="MainContent" Margin="0,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid Background="{ThemeResource SystemControlBackgroundAccentBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="110*"/>
<RowDefinition Height="43*"/>
<RowDefinition Height="47*"/>
</Grid.RowDefinitions>
<local:NumberPicker Margin="100,0,750,0" RangeBottom="0" RangeTop="20" SelectedValue="{Binding ElementName=PageElement,Path=ViewModel.Minutes,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></local:NumberPicker>
</Grid>
</ContentPresenter>
</Page>
Page.xaml.cs:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using UWPApp.Scorekeeper.Models.TransportClasses;
using UWPApp.Scorekeeper.Models.ViewModels;
namespace UWPApp.Scorekeeper
{
public sealed partial class SelectPenaltyTime : Page
{
public GameStateModel StateModel { get; set; }
public AddPenalty_FVM ViewModel { get; set; }
public SelectPenaltyTime()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var message = e.Parameter as PenaltyMessage;
StateModel = message.StateModel;
ViewModel = message.ViewModel;
}
private void NumberPicker_Loaded(object sender, RoutedEventArgs e)
{
var picker = sender as NumberPicker;
picker.SelectedValue = ViewModel.Minutes;
}
private void NumberPicker_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var picker = sender as NumberPicker;
ViewModel.Minutes = picker.SelectedValue;
}
}
}
AddPenalty_FVM:
public class AddPenalty_FVM
{
public int? Minutes { get; set; }
}
あなたのViewModelはどこですか?INotifyPropertyインターフェイスを実装していますか? – RTDev
それは必要ないはずですよね?値はページが読み込まれたときに読み込まれるだけなので、つまり、セレクタがviewmodelプロパティを設定するだけです。 – Ceshion
これは 'x:Bind'で正しく動作するので、これは' Binding'の制限です。 – Ceshion