I want to bind both the SelectedText and SelectedItem properties of an AutocompleteBox because my client wants to be able to input text and select from the list also. It's working properly but ...
The MainPage has one DataGrid. When I select a record from the Grid (i.e. SelectedItem), I want to set it in a popup window's AutocompleteBox. Some times it works but some times it doesn't.
What should I do for this issue?
This is my XAML:
<Sdk:AutoCompleteBox Grid.Column="3" Grid.Row="3" Height="18" Width="150"
IsTextCompletionEnabled="True" TabIndex="9" HorizontalAlignment="Left"
Text="{Binding ElementName=ResEdit,Path=DataContext.SelectedDemoText,Mode=TwoWay}"
ItemsSource="{Binding ElementName=ResEdit,Path=DataContext.DemoList,Mode=OneWay}"
ItemTemplate="{StaticResource DemoTemplate}"
ValueMemberPath="DemoCode"
LostFocus="AutoCompleteBox_LostFocus"
Margin="0,0,21,0" Padding="0">
</Sdk:AutoCompleteBox>
This property is in my view-model and bound to the DataGrid:
public InvoicesDTO SelectedInvoice
{
get { return _selectedInvoice; }
set
{
SelectedInvoice = value;
SelectedDomoText = SelectedInvoice.DemoText.Trim();
RaisePropertyChanged("SelectedInvoice");
}
}
You should not use both function SelectedText and SelectedItem in autocomplete. it's a bug of AutoCompleteBox..... A better way is to set the visiblity of the textbox and AutoCompleteBox on GotFocus and LossFocus. This Way You Will Defiantly Solve You Problem
private void DemoAutoComplete_LostFocus(object sender, RoutedEventArgs e)
{
DemoTextBox.Visibility = Visibility.Visible;
DemoAutoComplete.Visibility = Visibility.Collapsed;
DemoTextBox.Text = OCRAutoComplete.Text;
((DemoVM)this.DataContext).SelectedDemoText = DemoAutoComplete.Text;
}
private void DemoTextBox_GotFocus(object sender, RoutedEventArgs e)
{
DemoAutoComplete.Text = OctTextBox.Text;
DemoTextBox.Visibility = Visibility.Collapsed;
DemoAutoComplete.Visibility = Visibility.Visible;
DemoAutoComplete.Focus();
}
Related
I have this strange problem, where the binding seems completely ignored.
my xaml
<Button IsEnabled="{Binding ButtonEnabled}" x:Name="ButtonOK" BackgroundColor="Green" TextColor="White" Text="OK"/>
my C#
private bool _buttonEnabled = false;
public bool ButtonEnabled
{
get
{
// breakpoint 1, which never hits with value = false
return _buttonEnabled;
}
set
{
// breakpoint 2, which hits
_buttonEnabled = value;
OnPropertyChanged(nameof(ButtonEnabled));
}
}
private void ChassisEntry_TextChanged(object sender, TextChangedEventArgs e)
{
ButtonEnabled = ChassisEntry.Text != "";
}
private void PageScan_Appearing(object sender, EventArgs e)
{
ChassisEntry.Text = "";
}
I expect that when this page opens that ButtonOK is disabled, but it is not.
When I set breakpoints then breakpoint 1 (in the getter) never hits, its like if the xaml IsEnabled="{Binding ButtonEnabled}" is ignored.
The breakpoint 2 does hits, with value = false
What am I missing here ?
I googled this problem and found many similar questions, but all solutions given do not help with my problem.
Button IsEnabled binding not working properly
How to disable a button until all entries are filled?
Disable/Enable save button based on the mandatory field being null or empty using Behaviors
and many more
I am guessing you are using the xaml.cs page for holding your Bindings and hence if you are doing that there are two ways to do this
Set the BindingContext to the current class in the constructor before or right after InitializeComponent
BindingContext= this;
Or In your XAML
<ContentPage
....
x:Name="currentPage">
And in your button
<Button IsEnabled="{Binding ButtonEnabled, Source={x:Reference currentPage}}"
I would go with this: Set my Button in my XAML disabled.
<Button IsEnabled="False" x:Name="ButtonOK" BackgroundColor="Green" TextColor="White" Text="OK"/>
Then on my Entry control i would add the property TextChanged.
<Entry x:Name="ChassisEntry"
PlaceholderColor="DarkGray"
TextChanged="ChassisEntryChanged">
On xaml.cs file:
private void ChassisEntryChanged(object sender, TextChangedEventArgs e)
{
if (e.NewTextValue.Text != "")
{
ButtonOK.IsEnabled = true;
}
else
{
ButtonOK.IsEnabled = false;
}
}
I want to show a label when i click on my item in my listview.
The real problem i don't know how to link between my viewmodel and my views
I want modify my label in viewmodel but I don't know if its possible currently.
My xaml :
<StackLayout>
<Label x:Name="labelperso"
Text="{Binding newProduct}"
IsVisible="{Binding Addproduct}"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
BackgroundColor="#000000"
FontSize="20"
Opacity="0"/>
<ListView ItemsSource="{Binding Products}" CachingStrategy="RecycleElement" RowHeight="50" >
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding CodeReferenceLibelle}" TextColor="Black"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Behaviors>
<b:EventToCommandBehavior EventName="ItemSelected" Command="{Binding
SelectCommand}" Converter="{StaticResource SelectedItemConverter}"/>
</ListView.Behaviors>
my viewmodel :
#region labelperso property
private string _newProduct;
public string newProduct
{
get { return _newProduct; }
set { SetProperty(ref _newProduct, value); }
}
#endregion
#region Addproduct property
private bool _Addproduct;
public bool Addproduct
{
get { return _Addproduct; }
set { SetProperty(ref _Addproduct, value); }
}
#endregion
when I click on my item :
async Task Select()
{
newProduct = "Produit ajouté !";
basketManager.AddProductSkuAsync(sku);
newProduct = "";
await Task.Run(() => ShowText());
}
//I have tried this but I can't use my label in my view
async Task ShowText()
{
await labelperso.FadeTo(1);
await Task.Delay(1000);
await labelperso.FadeTo(0);
}
Why are you want to take the label "labelperso" in VM ? you can use it in xaml.cs instead.
You just need to add the event ItemSelected like this:
<ListView ItemsSource="{Binding Products}" ItemSelected="OnSelection">
In xaml.cs
void OnSelection(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null)
{
return;
}
//suppose the binding Object is Product
Product product = (Product)e.SelectedItem;
//labelperso.Text = "name = " + product.Name;
labelperso.FadeTo(1);
Task.Delay(1000);
labelperso.FadeTo(0);
}
Normally, VM are unrelated to Xaml, and we should not get labels from VM.
And we don't recommend it.But if you must, you can pass the Label in from the xaml.cs file like this:
You can define a variable in yourpage.xaml.cs:
public Label pageLabel;
and initial like this:
pageLabel = labelperso;
BindingContext = new YourViewmodel(this);
And in YourViewmodel.cs:
public Label ss;
public YourViewmodel(ContentPage parentPage)
{// here HomePage is your contentPage name of the page`
ss = ((HomePage)parentPage).pageLabel;//after this you can use it
}
You need to add a SelectedProduct property to your VM.
private string _SelectedProduct;
public string SelectedProduct
{
get { return _SelectedProduct; }
set { SetProperty(ref _SelectedProduct, value); }
}
You can then bind your ListView's SelectedItem to it
<ListView ItemsSource="{Binding Products}"
SelectedItem="{Binding SelectedProduct}"
CachingStrategy="RecycleElement"
RowHeight="50" >
You can then control the visibility of your label by binding to SelectedProduct via a "nullToVisibility" converter, or by using triggers etc.
You should try to use MVVM pattern rather than hacking with code behind.
Using MVVM you can add a Visible property to your viewmodel and bind the IsVisible property of the label to it.
Code will be much easy to read and maintain.
I am proto-typing a (Windows 10 universal app) UI and have built a very simple/rough user control to act as a 'badge', i.e show a numeric value in a circle, and animate the value changing. My issue is the control works if there is only a single instance of it in an application page. If there are multiple instances (even if the other instances are invisible), then only the last declared instance animates.
I've tried declaring the animation both in the user control's XAML, and in code behind to attempt to ensure there's no cross-over/mix up with the animation being shared. I also added a change callback to the property being animated, which writes the property value out using Debug.WriteLine. For control instances that animate properly the value changes as expected, i.e if we go from 10 to 20, the property is set to 10, 11, 12, 13.... 20. For the instances that don't work, the value is just set to the from property every time, i.e 10, 10, 10, 10, 10.
Below is a sample of the user control, and then a sample page that uses three instances of it. Placing both of these is a new Windows 10 Universal app called App3 should reproduce the issue. In the sample page the first two badges don't animate properly when their button is clicked, but the last one does.
Is anyone able to point out what I'm doing wrong, and why this breaks with multiple instances on a page?
Thanks.
Note: The code has gotten quite rough as I've hacked things around trying to figure out what the issue is, and it was only prototype code to begin with, so I apologize for the mess.
<UserControl
x:Class="App3.BadgeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App3"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="20"
d:DesignWidth="20">
<Grid>
<Ellipse x:Name="Border" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="{Binding BadgeBorderBrush}" />
<Ellipse x:Name="BadgeInner" Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="{Binding BadgeFillBrush}" />
<TextBlock x:Name="BadgeValue" Margin="5" HorizontalAlignment="Center" FontSize="10" VerticalAlignment="Center" TextAlignment="Center" TextTrimming="CharacterEllipsis" Foreground="White" Text="{Binding DisplayValue}" />
</Grid>
public sealed partial class BadgeView : UserControl
{
public DependencyProperty BadgeBorderBrushProperty = DependencyProperty.Register("BadgeBorderBrush", typeof(Brush), typeof(BadgeView), new PropertyMetadata(new SolidColorBrush(Windows.UI.Colors.Yellow)));
public DependencyProperty BadgeFillBrushProperty = DependencyProperty.Register("BadgeFillBrush", typeof(Brush), typeof(BadgeView), new PropertyMetadata(new SolidColorBrush(Windows.UI.Colors.Orange)));
public DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(BadgeView), new PropertyMetadata(0, new PropertyChangedCallback(ValueChanged)));
public DependencyProperty DisplayValueProperty = DependencyProperty.Register("DisplayValue", typeof(int), typeof(BadgeView), new PropertyMetadata(0, DisplayValueChanged));
private static void DisplayValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine(((BadgeView)d).DisplayValue);
}
private Storyboard AnimateBadgeValueCount;
private DoubleAnimation BadgeValueAnimation;
public BadgeView()
{
this.InitializeComponent();
this.BadgeValue.DataContext = this.BadgeInner.DataContext = this.Border.DataContext = this;
AnimateBadgeValueCount = new Storyboard(); ;
AnimateBadgeValueCount.Duration = TimeSpan.FromSeconds(0.5);
Storyboard.AllowDependentAnimations = true;
BadgeValueAnimation = new DoubleAnimation();
BadgeValueAnimation.Duration = TimeSpan.FromSeconds(0.5);
BadgeValueAnimation.EnableDependentAnimation = true;
BadgeValueAnimation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };
this.AnimateBadgeValueCount.FillBehavior = FillBehavior.Stop;
this.BadgeValueAnimation.FillBehavior = FillBehavior.Stop;
AnimateBadgeValueCount.Children.Add(BadgeValueAnimation);
Storyboard.SetTarget(AnimateBadgeValueCount, this);
Storyboard.SetTargetProperty(AnimateBadgeValueCount, "DisplayValue");
this.AnimateBadgeValueCount.Completed += AnimateBadgeValueCount_Completed;
}
private void AnimateBadgeValueCount_Completed(object sender, object e)
{
this.DisplayValue = this.Value;
}
private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var badgeView = (BadgeView)d;
badgeView.AnimateValue();
}
private void AnimateValue()
{
if (Value != DisplayValue)
{
this.BadgeValue.DataContext = this.BadgeInner.DataContext = this.Border.DataContext = this;
this.AnimateBadgeValueCount.Stop();
this.BadgeValueAnimation.From = this.DisplayValue;
this.BadgeValueAnimation.To = this.Value;
this.BadgeValueAnimation.FillBehavior = FillBehavior.Stop;
//Storyboard.SetTarget(this.AnimateBadgeValueCount, this);
//Storyboard.SetTargetProperty(this.AnimateBadgeValueCount, "DisplayValue");
this.AnimateBadgeValueCount.Begin();
}
}
public Brush BadgeBorderBrush
{
get { return (Brush)this.GetValue(this.BadgeBorderBrushProperty); }
set
{
this.SetValue(this.BadgeBorderBrushProperty, value);
}
}
public Brush BadgeFillBrush
{
get { return (Brush)this.GetValue(this.BadgeFillBrushProperty); }
set
{
this.SetValue(this.BadgeFillBrushProperty, value);
}
}
public int Value
{
get { return (int)this.GetValue(ValueProperty); }
set
{
this.SetValue(ValueProperty, value);
}
}
public int DisplayValue
{
get { return (int)this.GetValue(DisplayValueProperty); }
set
{
this.SetValue(DisplayValueProperty, value);
}
}
}
<Page
x:Class="App3.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App3"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Orientation="Vertical">
<Button Content="Do it" x:Name="DoIt1" Click="DoIt1_Click" />
<local:BadgeView x:Name="Badge1" Width="20" Height="20" BadgeFillBrush="Blue" />
<Button Content="Do it" x:Name="DoIt2" Click="DoIt2_Click" />
<local:BadgeView x:Name="Badge2" Width="20" Height="20" />
<Button Content="Do it" x:Name="DoIt3" Click="DoIt3_Click" />
<local:BadgeView x:Name="Badge3" Width="20" Height="20" />
</StackPanel>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void DoIt1_Click(object sender, RoutedEventArgs e)
{
this.Badge1.Value += 10;
}
private void DoIt2_Click(object sender, RoutedEventArgs e)
{
this.Badge2.Value += 10;
}
private void DoIt3_Click(object sender, RoutedEventArgs e)
{
this.Badge3.Value += 10;
}
}
What I have done is simplify code and simplify and move the datacontext inside the loaded event inside the constructor:
this.Loaded += (s, e) =>
{
this.DataContext = this;
AnimateBadgeValueCount = new Storyboard(); ;
AnimateBadgeValueCount.Duration = TimeSpan.FromSeconds(0.5);
BadgeValueAnimation = new DoubleAnimation();
BadgeValueAnimation.Duration = TimeSpan.FromSeconds(0.5);
//BadgeValueAnimation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };
AnimateBadgeValueCount.Children.Add(BadgeValueAnimation);
Storyboard.SetTarget(AnimateBadgeValueCount, this);
Storyboard.SetTargetProperty(AnimateBadgeValueCount, "DisplayValue");
this.AnimateBadgeValueCount.Completed += AnimateBadgeValueCount_Completed;
};
private async void AnimateValue()
{
if (Value != DisplayValue)
{
this.AnimateBadgeValueCount.Stop();
this.BadgeValueAnimation.From = this.DisplayValue;
this.BadgeValueAnimation.To = this.Value;
BadgeValueAnimation.EnableDependentAnimation = true;
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{
this.AnimateBadgeValueCount.Begin();
});
}
}
I have commented the EasingFunction, it works but in my opinion fit betters.
It is curious, because if I set the datacontext just in the constructor it works bad, but inside goes fine.
Tell me if you try.
I have a list of items - when one is selected, I'd like to slide the appbar up. This is what I have so far:
<AppBar IsOpen="{Binding BookIsSelected}">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<AppBarButton Icon="Remove" Label="Remove Book" Command="{Binding RemoveBook}" Visibility="{Binding BookIsSelected, Mode=TwoWay, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</Grid>
</AppBar>
In the gridview of books, I have this:
SelectedItem ="{Binding SelectedBook, Mode = TwoWay}" />
BookIsSelected:
private bool _bookSelected;
public bool BookIsSelected {
get {
return _bookSelected;
}
set {
SetProperty(ref _bookSelected, value);
}
}
I can see the get/set hit when I select a book, but the appbar never flies out. What am I doing wrong?
I can't see any obvious problems with the code that you've shown so far. However, you haven't shown the code for the SelectedBook property or SetProperty method, so perhaps your problem lies there? Your SelectedBook property should look something like this, setting BookIsSelected to true:
private YourDataType _selectedBook;
public YourDataType SelectedBook{
get {
return _selectedBook;
}
set {
SetProperty(ref _selectedBook, value);
BookIsSelected = true;
}
}
However, unless you set the BookIsSelected property back to false at some stage, this will only work once. Your SetProperty method should notify the INotifyPropertyChanged interface of property changes, as #Amer mentioned in the comments. It should look something like this example from the linked page:
private void SetProperty([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I've searched a bit, but the info I've found isn't what I need. So I decided to ask you all - I'm sure it's a newbie question but i really don't get it.
Let's start:
I have a DataSource which is a grouped observable collection. At the moment I've 2 groups with a different count of items. The two groups and the items belong to the same common base:
public DataCommon(String uniqueId, String title, String subtitle, String imagePath, String description)
{
this._uniqueId = uniqueId;
this._title = title;
this._subtitle = subtitle;
this._description = description;
this._imagePath = imagePath;
}
This is the constructor of the model.
In the ViewModel I fill it.
Now I would like bind the ItemClick with a Command to my ViewModel. I do like this (only a short part):
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="2"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
>
<WinRtBehaviors:Interaction.Behaviors>
<Win8nl_Behavior:EventToCommandBehavior Event="ItemClick" Command="ItemClickCommand" CommandParameter="{Binding UniqueId}"/>
</WinRtBehaviors:Interaction.Behaviors>
But now the problem. At the "Binding UniqueId" it's saying the DataContext is my ViewModel, so i can't connect it to the properties of the Model. Looked at the Page.DataContext i told XAML tu use my ViewModel as DataContext. I guess this was correct. But how can I access the Model-properties?
I've tried to do it like this (defined my Model as DataModel):
<WinRtBehaviors:Interaction.Behaviors>
<Win8nl_Behavior:EventToCommandBehavior Event="ItemClick" Command="ItemClickCommand" CommandParameter="{Binding DataModel:SampleDataCommon.UniqueId}"/>
</WinRtBehaviors:Interaction.Behaviors>
but as I guessed beforehand it didn't work - as parameter i get null.
I would be thankful for any help, because as i said at the beginning of the post: I really don't get it...
You can't use EventToCommandBehavior in this way - this was also stated by its author in the comments.
I'm using the following attached property in such cases:
public static class ItemClickBehavior
{
public static DependencyProperty ItemClickCommandProperty = DependencyProperty.RegisterAttached("ItemClickCommand",
typeof(ICommand),
typeof(ItemClickBehavior),
new PropertyMetadata(null, OnItemClickCommandChanged));
public static void SetItemClickCommand(DependencyObject target, ICommand value)
{
target.SetValue(ItemClickCommandProperty, value);
}
public static ICommand GetItemClickCommand(DependencyObject target)
{
return (ICommand)target.GetValue(ItemClickCommandProperty);
}
private static void OnItemClickCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
var element = target as ListViewBase;
if (element != null)
{
// If we're putting in a new command and there wasn't one already
// hook the event
if ((e.NewValue != null) && (e.OldValue == null))
{
element.ItemClick += OnItemClick;
}
// If we're clearing the command and it wasn't already null
// unhook the event
else if ((e.NewValue == null) && (e.OldValue != null))
{
element.ItemClick -= OnItemClick;
}
}
}
static void OnItemClick(object sender, ItemClickEventArgs e)
{
GetItemClickCommand(sender as ListViewBase).Execute(e.ClickedItem);
}
}
This is how you would bind a command to it:
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="2"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
itbmb:ItemClickBehavior.ItemClickCommand="{Binding ItemClickCommand}"
>
I guess it wouldn't be all that difficult to create a behavior from the attached property if you really wanted to.