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.
Related
I have a TabControl where I create tabs dynamically. I am finding it difficult to change the title of the TabItem.
<TabControl Name="AttorneysTabControl" Grid.Column="2" Grid.Row="0">
<TabControl.Resources>
<DataTemplate x:Key="AttorneyTabHeader">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Names}" Margin="2,0,0,0" FontSize="16" VerticalAlignment="Center" />
<Button Width="Auto" UseLayoutRounding="False" BorderBrush="Transparent" Background="Transparent" Click="CloseAttorneysTabButtonClick">
<Image Source="/images/close-cross-thin-circular-button/close-cross-thin-circular-button16.png" Height="16"></Image>
</Button>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="AttorneyTabContent">
<local:AttorneyDetails></local:AttorneyDetails>
</DataTemplate>
</TabControl.Resources>
For each TabItem I set a HeaderTemplate from the TabControl.Resources like this;
newTabItem.HeaderTemplate = (System.Windows.DataTemplate)AttorneysTabControl.FindResource("AttorneyTabHeader");
But I don't know how to change the contents of the TabItem header once the template has been set. I have tried using DataContext for the TabItem if that's the way to do it but it did not work, so that I could just use Binding in the template. That will be a lot easier.
You should normally write (first line is your unchanged code):
newTabItem.HeaderTemplate = (System.Windows.DataTemplate)AttorneysTabControl.FindResource("AttorneyTabHeader");
var tabItemData = new TabItemData() { Name="Initial name"} ;
newTabItem.DataContext = tabItemData;
And then once you need to update the tab header:
tabItemData.Name = "New name".
If that didn't work, that'd probably because your TabItemData.Name property doesn't notify of its changes. So make sure that your TabItemData class implements INotifyPropertyChanged and that the Name property notifies. Example:
public class TabItemData : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return this.name; }
set
{
if (value != this.name)
{
this.name= value;
NotifyPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In case you're lost I suggest reading the Managing data in a WPF application chapter of my Learn WPF MVVM book.
I have a pre-loader screen that essentially says "please wait" as I have server-side computation being processed for several seconds.
I have a value converter that should update and get rid of the loader screen once the server-side computation has been processed and stored.
WPF Portion
<Window.Resources>
<Client:BoolToVisibilityConverter x:Key="loadConverter"/>
</Window.Resources>
.
.
.
<Border Panel.ZIndex="1000" BorderBrush="Yellow" BorderThickness="1" Visibility="{Binding OverlayVisibility, Converter={StaticResource loadConverter}, Mode=TwoWay}" Background="#80000000" Margin="0,0,0,-25.6">
<Grid>
<TextBlock Panel.ZIndex="100" Margin="0" TextWrapping="Wrap" Text="Loading Passive Seismic Nodes..." HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="21" FontWeight="Bold" Foreground="#FFF"/>
<TextBlock Panel.ZIndex="100" Margin="11,136,12,75.2" TextWrapping="Wrap" Text="Please Wait..." HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="14" FontWeight="Bold" Foreground="#FFF"/>
</Grid>
</Border
I have an OverlayVisibility property in this class that is a boolean value to help toggle the preloader screen.
Portion of the Client Class
public void LoadRoles()
{
foreach (var roleName in ChefServer.GetCookbookNames())
{
Cookbooks.Add(new Cookbook() { CookbookName = roleName });
}
//This isn't making the preloader disappear
uiContext.Send((_ => { overlayVisibility = false; }), null);
Console.WriteLine("Done!"); //This gets called successfully
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
//This function gets called when WPF form loads
public void Loader()
{
uiContext = SynchronizationContext.Current; //Declared at top in namespace
OverlayVisibility = true; //Make preloader screen show at boot
}
#region Props
private bool overlayVisibility;
public bool OverlayVisibility
{
get { return overlayVisibility; }
set
{
overlayVisibility = value;
OnPropertyChanged("OverlayVisibility");
}
}
#endregion
You're setting overlayVisibility (the field), not OverlayVisibility (the property).
Therefore, you never actually raise PropertyChanged, and WPF never finds out.
Are you sure you have set up DataContext correctly? try adding the following line to your c'tor if you have not set it up yet
this.DataContext = this;
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)
This must be something obvious, but can anyone tell me why my the value in my label is only updated once. My PropertyChangedEventHandler never fires:
<Page.Resources>
x:Key="SoSummaryViewModelDataSource"/>
</Page.Resources>
<Grid DataContext="{StaticResource SoSummaryViewModelDataSource}">
<Label Grid.Row="2"
Margin="30, 0, 0, 0"
FontWeight="Medium"
Content="{Binding TotalDisplayedCustomers, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
Here is my property:
public string TotalDisplayedCustomers
{
get { return _totalDisplayedCustomers; }
set
{
if (_totalDisplayedCustomers != value)
{
_totalDisplayedCustomers = value;
OnPropertyChanged("TotalDisplayedCustomers");
}
}
}
And here is my OnPropertyChanged:
protected void OnPropertyChanged(string propertyName)
{
//when propertyName is TotalDisplayedCustomers, handler is null, why??
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Ha you tried to inspect yout ViewModel load into your DataContext. When i want to inspect it, i use the Wpf Inspector Tools
Well here is what I came up with, I still don't understand what was wrong with my Binding, but instead of relying on the PropertyChanged firing on a string in my view model, I instead bound my Run to the Count property of an ObservableCollection.
First I used a DataContext on my page:
<Page.DataContext>
<vm:SoSummaryViewModel/>
</Page.DataContext>
Changed my TextBlock like this:
<TextBlock Grid.Row="2" Margin="40, 0, 0, 0">
<Run Text="Customer Count: " FontWeight="Medium"></Run>
<Run Text="{Binding SummaryLineItems.Count, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}">
<TextBlock Text=" (Filtered)" Visibility="{Binding HideOnTimeCustomers, Converter={StaticResource showIfTrue}}"/>
</TextBlock>
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");
}