I created a simple XAML page:
<Page.DataContext>
<local:MainPageViewModel />
</Page.DataContext>
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding MyNumber}" Value="3">
<Core:CallMethodAction MethodName="TestMethod" TargetObject="{Binding ElementName=page}" />
<Core:CallMethodAction MethodName="ViewModelMethod" TargetObject="{Binding Mode=OneWay}" />
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<Grid>
<TextBox
Margin="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding MyNumber,
Mode=TwoWay}"
TextWrapping="Wrap" />
</Grid>
And a ViewModel for this page:
public class MainPageViewModel : INotifyPropertyChanged
{
private int _myNumber;
public int MyNumber
{
get { return _myNumber; }
set
{
_myNumber = value;
RaisePropertyChanged("MyNumber");
Debug.WriteLine("Property MyNumber changed.");
}
}
public void ViewModelMethod()
{
Debug.WriteLine("ViewModelMethod called.");
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion INotifyPropertyChanged
}
In the page code behind, I added the following method:
public void TestMethod()
{
Debug.WriteLine("Method TestMethod called.");
}
When I enter number 3 in the TextBox, only the second CallMethodAction gets executed.
The TextMethod in the code behind doesn't execute. Why? I never had this problem in Windows Phone 8.
Found a solution! In Windows Phone 8.0, you have to set the TargetObject property to call methods in the code behind. In Windows Phone 8.1, you don't set the TargetObject at all. The code that works is:
<Core:CallMethodAction MethodName="TestMethod" />
Though, I still think it should at least throw an error, if a method doesn't exist...
This works for me, Windows Phone 8.1...
XAML page
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Tapped">
<Core:CallMethodAction MethodName="TestMethod" TargetObject="{Binding ElementName=page}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
Is base on Tappe event, but I think in will works on your case.
For the method, important! make it public.
public void TestMethod(object sender, TappedRoutedEventArgs e)
{
Debug.WriteLine("TestMethod");
}
Related
I have a UWP application where the buttons hold state work just fine until I bind (any) buttons visibility or editable state where the hold state goes away. The buttons only work if I tap super quickly after something gets binded. If I switch to a different view then back to the original view, the buttons magically work again. Anyone have any ideas what could be causing this?
Added the IsHoldingState="true" to parent and button in xaml.
xaml code
<controls:RoundedButton Grid.Row="2" Style="{StaticResource SubmitButtonStyle}" Command="{Binding SubmitCommand}" VerticalAlignment="Bottom" Visibility="{Binding IsNotEmpty, Converter={StaticResource visibilityConverter}, ConverterParameter=false}" Margin="-16,-16,-32,-32" Width="384" Height="112" Opacity="0" Background="Transparent" />
<controls:RoundedButton Grid.Row="2" Style="{StaticResource SubmitButtonStyle}" Command="{Binding SubmitCommand}" VerticalAlignment="Bottom" Visibility="{Binding IsNotEmpty, Converter={StaticResource visibilityConverter}, ConverterParameter=false}">
cs code (where the binding is updated via onpropertychanged)
private bool isNotEmpty;
public bool IsNotEmpty
{
get { return isNotEmpty; }
set { Set(() => IsNotEmpty, ref isNotEmpty, value); }
}
protected bool Set<T>(Expression<Func<T>> selectorExpression, ref T field, T value)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
RaisePropertyChanged(selectorExpression);
return true;
}
protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
var propertyName = GetPropertyName(selectorExpression);
OnPropertyChanged(propertyName);
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
The problem may be in your ConverterParameter, ConverterParameter can't be directly assigned to a boolean value, please try this:
<Page.Resources>
<x:Boolean x:Key="DefaultParameter">False</x:Boolean>
</Page.Resources>
...
<controls:RoundedButton Visibility="{Binding IsNotEmpty, Converter={StaticResource visibilityConverter}, ConverterParameter={StaticResource DefaultParameter}}">
Best regards.
Basically what i have is a ListBox with ContextMenu
<ListBox Margin="2,0,0,0" Grid.Row="1" ItemsSource="{Binding MyCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource NoVisualButton }" Tag="{Binding ID}" Width="430" toolkit:TiltEffect.IsTiltEnabled="True" Margin="0,0,0,12" Click="OnSelectWorkOutItemClick">
<StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="delete" Tag="{Binding ID}" Click="onContextMenuDeleteItemClick" IsEnabled="{Binding IsDeleteOptionEnable, ElementName=LayoutRoot}"/>
<toolkit:MenuItem Header="edit" Tag="{Binding ID}" Click="onContextMenuItemEditClick" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
...
</StackPanel>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So if MyCollection has only one item, i have to disable delete MenuItem.
My model has a property
public bool IsDeleteOptionEnable
{
get
{
return MyCollection.Count() >= 2;
}
}
In the page i am setting the DataContext like:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (IsDataLoaded)
{
this.DataContext =MyModel;
}
}
The listbox is getting populated, but i can't disable "delete" MenuItem. What am i doing wrong?
Since the IsDeleteOptionEnable is a regular property, your view won't get notified when the property is changed. On options would be implementing INotifyPropertyChanged in your model (actually that should be ViewModel in an MVVM pattern) and calling the PropertyChanged event whenever items in your collection gets changed.
class YourModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
..
..
public YourModel() {
this.MyCollection = ...;
this.MyCollection.CollectionChanged += MyCollection_CollectionChanged;
}
public bool IsDeleteOptionEnable {
get {
return MyCollection.Count() >= 2;
}
}
private void MyCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
this.OnPropertyChanged("IsDeleteOptionEnable");
}
private void OnPropertyChanged(string name = null) {
if (this.PropertyChanged != null) {
PropertyChangedEventArgs ea = new PropertyChangedEventArgs(name);
this.PropertyChanged(this, ea);
}
}
}
Now when an item get removed or added to the collection, the model raises and PropertyChanged event so that the view will be aware that the IsDeleteOptionEnable property is (actually might) changed, and the enabled state of the button gets updated.
Try
IsEnabled="{Binding DataContext.IsDeleteOptionEnable, ElementName=LayoutRoot}"
As DataSource you need to use ObservableCollection. Then you need to implement INotifyPropertyChanged -interface in the class which contains the binded Property.
Example Class:
// Example of binded object
public class MyItem: INotifyPropertyChanged {
// Binded Property
private String itemIsVisible = "Yes";
public String ItemIsVisible{
get { return itemIsVisible; }
set {
itemIsVisible = value;
// This ensures the updating
OnPropertyChanged("ItemIsVisible");
}
}
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Example XAML:
<TextBlock Text="{Binding ItemIsVisible}" />
I have been facing a issue in updating the XAML in windows phone 8... the properties are binded in XAML with the viewModel, propertyChange is triggered and it changes the values of the properties. but the property members in XAML are only updated once at the beginning since then it does not update any thing in XAML... Although the properties continue to change in ViewModel.... the properties belong to a LIST of observation collection and finally Observation Collection is binded to LongListSelector
I have changed the binding Mode to "two Way" but useless i have pasted the code below.
Looking forward for help.
ViewModel:
private string _description;
public string description
{
set
{
_description = value;
RaisePropertyChanged("_description");
}
get
{
return _description;
}
}
private double _progress_bar_Value;
public double progress_bar_Value
{
set
{
_progress_bar_Value = value;
RaisePropertyChanged("_progress_bar_Value");
}
get
{
return _progress_bar_Value; //= ProfileSetting.ProfileTab_DOB;
}
}
private double _Total_Bytes;
public double Total_Bytes
{
set
{
_Total_Bytes = value;
RaisePropertyChanged("_Total_Bytes");
}
get
{
return _Total_Bytes;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
`
>
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,0" Orientation="Vertical"
>
<TextBlock Text="{Binding description}"
FontSize="18"
TextWrapping="Wrap"
Foreground="White" x:Name="Totalsize"
/>
<ProgressBar x:Name="Download_progressBar"
IsIndeterminate="False"
Maximum="100"
Height="10"
Width="400"
Value="{Binding progress_bar_Value}"
Foreground="White"
/>
<TextBlock Text="{Binding Bytes_received}"
FontSize="18"
TextWrapping="Wrap"
Foreground="White"
x:Name="Total_received"
/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>`
Raise Property Changed on the public property not backing field (as commented by #HighCore)
I have binding ObservableCollection - DataGrid (mode - TwoWay), but i want update collection by myself with UpdateSource() call and disable automathic source updating. I set binding like
ItemsSource="{Binding Path=Bezier.BezierPoints, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
but my collection still updates automathically. My code samples are below. What am i doing wrong?
My XAML:
<DataGrid Name="BezierPointsDataGrid" Margin="5" AutoGenerateColumns="False"
Grid.Column="0" Grid.Row="0" Background="White"
ItemsSource="{Binding Path=Bezier.BezierPoints, Mode=TwoWay, UpdateSourceTrigger=Explicit}">
<DataGrid.Columns>
<DataGridTextColumn Header="X" Binding="{Binding Path=X}" Width="1*"/>
<DataGridTextColumn Header="Y" Binding="{Binding Path=Y}" Width="1*"/>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding Path=UpdateBezierPointsCommand}" CommandParameter="{Binding ElementName=BezierPointsDataGrid}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
My ViewModel:
class BezierCurveViewModel : INotifyPropertyChanged
{
#region Bezier curve model
private BezierCurveModel _bezier;
public BezierCurveModel Bezier
{
get { return _bezier; }
set
{
if (_bezier == value)
return;
_bezier = value;
OnPropertyChanged("Bezier");
}
}
#endregion
#region Commands
public ICommand UpdateBezierPointsCommand { set; get; }
#endregion
#region Constructor
public BezierCurveViewModel()
{
UpdateBezierPointsCommand = new Command(a => ((DataGrid)a).GetBindingExpression(DataGrid.ItemsSourceProperty).UpdateSource());
Bezier = new BezierCurveModel();
}
#endregion
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
My model:
public ObservableCollection<DPoint> BezierPoints { private set; get; }
EDIT: I changed ObservableCollection To DataTable to achieve expected behaviour. But i am still interested in solving this problem because i want to understand why any binding to observable collection updates source after editing table (read my comment to Andrew's post).
Here, you have set up the view to update the BezierPoints property explicitly, since that is what you are binding the ItemsSource.
I will assume that what you actually want is to use an Explicit update trigger on the properties of the individual points. To do this, you need to change the DataGridTextColum binding to UpdateSourceTrigger=Explicit.
As a side note, it would seem impossible that you could ever update the BezierPoints collection from the View at all because the property has a private setter.
I have a strange binding problem on WP 7. Code works on WP8 without problems but when I run the same (following) code on WP7 binding does not work and TextBlock.Text is "". Here is the code (binding is set on the Text property of the second TextBlock):
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,35">
<ListBox x:Name="MainListBox" Margin="0,0,-12,0" SelectionChanged="MainListBox_SelectionChanged">
<StackPanel x:Name="MeasurementUnitPropertyPanel" toolkit:TiltEffect.IsTiltEnabled="True" Margin="12,0,0,0" Orientation="Horizontal" MinHeight="100">
<TextBlock x:Name="MeasurementUnitPropertyLabel" Width="235" Margin="0,30,0,0" HorizontalAlignment="Left" Text="{Binding Path=AppResources.MeasurementUnitPropertyLabel, Source={StaticResource LocalizedStrings}}" Style="{StaticResource PhoneTextLargeStyle}" FontSize="28">
<TextBlock.Foreground>
<SolidColorBrush Color="Black"/>
</TextBlock.Foreground>
</TextBlock>
<TextBlock x:Name="MeasurementUnitPropertyValue" Width="185" Margin="0,30,0,0" TextAlignment="Right" Text="{Binding MeasurementUnit}" Style="{StaticResource PhoneTextLargeStyle}" FontSize="28">
<TextBlock.Foreground>
<SolidColorBrush Color="{StaticResource DarkGrayThemeColor}"/>
</TextBlock.Foreground>
</TextBlock>
</StackPanel>
...
Then I set the DataContext in the OnNavigatedTo method (or in the constructor, problem is the same)...
// When page is navigated to set data context to selected item in list
protected override void OnNavigatedTo(NavigationEventArgs e)
{
viewModel = new ClimateSettingsViewModel();
DataContext = viewModel;
//MeasurementUnitPropertyValue.DataContext = viewModel.MeasurementUnit; //This does not work too...
//Other stuff...
}
(part of) ClimateSettingsViewModel class:
class ClimateSettingsViewModel : INotifyPropertyChanged
{
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its value using a Binding.
/// </summary>
/// <returns></returns>
public String MeasurementUnit
{
get
{
return ClimateSettings.MeasurementUnitValues[App.UserData.SelectedConfiguration.ClimateSettings.MeasurementUnit];
}
/*
set
{
if (value != ClimateSettings.MeasurementUnitValues[App.UserData.SelectedConfiguration.ClimateSettings.MeasurementUnit])
{
App.UserData.SelectedConfiguration.ClimateSettings.MeasurementUnit = value;
NotifyPropertyChanged("MeasurementUnit");
}
}*/
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
App platform is WP OS 7.1. Thanks in advance!
After further investigation, Windows Phone 7 and Windows Phone 8 have differently implemented reflection.
On Windows Phone 7, if you try to access private or internal functions, you will get a MethodAccessException but on the Windows Phone 8 it will just work.
Just turn on all exceptions when debugging and this error will jump up.