I'm pretty new to MVVM so i assume this is basics. It's a Windows 8.1 app with MVVM light and a Sqlite DB.
I've got a page that contain 4 States. Each State contain a GridView where you can select an item to set a bound property. On the SelectionChanged I go to the next State.
Here's the GridView's xaml :
<GridView Grid.Row="1" Grid.Column="1" ItemsSource="{Binding Essences}" SelectedItem="{Binding SelectedEssense,Mode=TwoWay}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="EssenceGridView" Opacity="0" >
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="SelectionChanged">
<Core:GoToStateAction StateName="Diametre"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<GridView.ItemTemplate>
<DataTemplate >
<Grid Width="250" Height="80">
<TextBlock Text="{Binding Trigramme}" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The StoryBoards that change States are just Opacity changing.
Now I want to be able to totaly skip one State, because the user want to be able to use some defaults properties (so he don't have to select the same one all the time)
So, if the default property is set in my ViewModel, I want to able to skip one state.
What is the best approach to achieve this in MVVM ?
Edit : With the help of the link of Depechie, I successfully bind my VisualState to property in my ViewModel. I had to update the code because it was for Windows phone 8 and i'm working with Windows 8.1 !!
Here the updated class :
BindVisualStateBehaviorHandler.cs
class BindVisualStateBehaviorHandler : Behavior<FrameworkElement>
{
//
// Dependency property "StateName" that you can bind in Blend.
// Bind this to the enumeration that controls the visual state.
//
public static DependencyProperty StateNameProperty = DependencyProperty.Register(
"StateName",
typeof(string),
typeof(BindVisualStateBehaviorHandler),
new PropertyMetadata(null, StateNamePropertyChanged));
public string StateName
{
get { return (string)GetValue(StateNameProperty); }
set { SetValue(StateNameProperty, value); }
}
//
// When the StateName property changes, switch to the
// new visual state, and play transition animations.
//
private static void StateNamePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
((BindVisualStateBehaviorHandler)obj).UpdateVisualState(
(string)args.NewValue,
useTransitions: true);
}
//
// When the behavior is first attached, go to the visual state,
// but don't play any animations.
//
protected override void OnAttached()
{
UpdateVisualState(
StateName,
useTransitions: false);
base.OnAttached();
}
private void UpdateVisualState(string visualState, bool useTransitions)
{
if (AssociatedObject != null)
{
if (VisualStateUtilities.FindNearestStatefulControl(base.AssociatedObject as FrameworkElement) != null)
{
VisualStateUtilities.GoToState(
VisualStateUtilities.FindNearestStatefulControl(base.AssociatedObject as FrameworkElement),
visualState,
useTransitions);
}
}
}
}
I had to use my own Behavior cuz it was not présent in Microsoft.Xaml.Interactivity
Behavior.cs
public abstract class Behavior<T> : DependencyObject, IBehavior where T : DependencyObject
{
//http://reflectionit.nl/Blog/2013/windows-8-xaml-tips-creating-blend-behaviors
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public T AssociatedObject { get; set; }
protected virtual void OnAttached()
{
}
protected virtual void OnDetaching()
{
}
public void Attach(Windows.UI.Xaml.DependencyObject associatedObject)
{
this.AssociatedObject = (T)associatedObject;
OnAttached();
}
public void Detach()
{
OnDetaching();
}
DependencyObject IBehavior.AssociatedObject
{
get { return this.AssociatedObject; }
}
}
Then in my View :
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="AppStates" >
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.2"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="SaisieTige" >
...
<VisualState x:Name="Diametre">
...
</VisualState>
<VisualState x:Name="Hauteur">
...
</VisualState>
<VisualState x:Name="Resume">
...
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Interactivity:Interaction.Behaviors>
<Behavior:BindVisualStateBehaviorHandler x:Name="AppStateBehavior" StateName="{Binding StateHandler.AppState,Mode=TwoWay}"></Behavior:BindVisualStateBehaviorHandler>
<Core:EventTriggerBehavior EventName="Loaded">
<Core:InvokeCommandAction Command="{Binding SelectEssenceOrNotCommand}"></Core:InvokeCommandAction>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
I have to call the RaisePropertyCHange each time i change the State :
StateHandler.SetAppState(AppStates.SaisieTige);
RaisePropertyChanged("StateHandler");
ANd Now it's working, thx a lot guys !!!
Related
My question relates to how to initiate as action based on user mouse input.
I have a wpf window that displays information on an organization. Some organizations are vendors in which case vendor information is displayed in another row of the grid. I use the following trigger to display/hide that row. This works as desired.
<RowDefinition>
<RowDefinition.Style>
<Style
TargetType="RowDefinition">
<Setter Property="Height" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsVendor}" Value="True">
<Setter Property="Height" Value="*" />
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Style>
</RowDefinition>
For an organization which is not a vendor, the user can click on a button that adds vendor information and links that new information to the organization. However, that action does not cause the trigger to fire. Is there a way to do that? Or is there a different approach that would work better?
In your case, I will use a converter and bind the value of Height property.
<RowDefinition>
<RowDefinition.Style>
<Style TargetType="RowDefinition">
<Setter Property="Height" Value="{Binding IsVendor, Converter={StaticResource IsVendorConverter}}" />
</Style>
</RowDefinition.Style>
</RowDefinition>
The converter will receive your boolean value (IsVendor) and returns "0" if IsVendor is false and "*" if IsVendor is true.
public class IsVendorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var isVendor = (bool)value;
if (isVendor)
{
return "*";
}
return "0";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You didn't share your xaml, but here is a sample used with a button (clicking one button changes the height of the other one).
<Window
x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Window.Resources>
<local:IsVendorConverter x:Key="IsVendorConverter" />
</Window.Resources>
<StackPanel Orientation="Horizontal">
<Button Width="200" Height="{Binding IsVendor, Converter={StaticResource IsVendorConverter}}" />
<Button Width="200" Height="100" Click="Button_Click"/>
</StackPanel>
</Window>
Code Behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private bool _isVendor = false;
public bool IsVendor {
get { return _isVendor; }
set { _isVendor = value; OnPropertyRaised("IsVendor"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyRaised(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
IsVendor = true;
}
}
I have a user control like this:
<UserControl>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<HyperlinkButton Grid.Row="0" />
<TextBlock Name="textblock" Grid.Row="1"
Text="{Binding dailyText, ElementName=userControl}">
</TextBlock>
</Grid>
</UserControl>
Nevertheless, I don't know, how can I set a style from mainwindow to user control? I have solved the problem to access to other properties like this:
public static readonly DependencyProperty MyContentProperty =
DependencyProperty.Register("MyContent", typeof(object), typeof(Day), null);
public object MyContent
{
get { return (object)GetValue(MyContentProperty ); }
set { SetValue(MyContentProperty , value); }
}
And then
<local:Day MyContent="Hello World" />
However, it doesn't work the style. There is no change in the sytle.
Thank you.
(Modification)
Below is a mainWindow part.
<Page.Resources>
<Style TargetType="TextBlock" x:Name="MyTextBlockStyle">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="SelectionHighlightColor" Value="Red"/>
<Setter Property="FontSize" Value="10"/>
</Style>
</Page.Resources>
<local:Day MyStyle="{StaticResource MyTextBlockStyle}">
Behind-code part in userControl
public static readonly DependencyProperty MyStyleProperty =
DependencyProperty.Register("MyStyle", typeof(Style), typeof(Day), null);
public Style MyStyle
{
get { return (Style)GetValue(MyStyleProperty); }
set { SetValue(MyStyleProperty, value); }
}
You can use PropertyMetadata to initialise and set Style to your TextBlock. Like,
public Style MyStyle
{
get { return (Style)GetValue(MyStyleProperty); }
set { SetValue(MyStyleProperty, value); }
}
public static readonly DependencyProperty MyStyleProperty =
DependencyProperty.Register("MyStyle", typeof(Style), typeof(Day),
new PropertyMetadata(null, OnStyleChange));
private static void OnStyleChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as Day;
control.textblock.Style = (Style)e.NewValue
}
I'm get an issue using the Template 10 Hamburger Template and Zxing.mobile.net library
"Template10": "1.1.12",
"ZXing.Net.Mobile": "2.1.46"
if I add the following code to the Mainpage and Viewmodel, the scanner works correctly and binds the value back to the TextBox but if I add the same code to the details page the returned code wont bind.I'm doing anything wrong.
Mainpage.xaml
<Page x:Class="WindowsApp3.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Behaviors="using:Template10.Behaviors"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:controls="using:Template10.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:WindowsApp3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:WindowsApp3.ViewModels"
mc:Ignorable="d">
<Page.DataContext>
<vm:MainPageViewModel x:Name="ViewModel" />
</Page.DataContext>
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
<VisualState x:Name="VisualStateNarrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<!-- TODO: change properties for narrow view -->
<Setter Target="stateTextBox.Text" Value="Narrow Visual State" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="VisualStateNormal">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<!-- TODO: change properties for normal view -->
<Setter Target="stateTextBox.Text" Value="Normal Visual State" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="VisualStateWide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<!-- TODO: change properties for wide view -->
<Setter Target="stateTextBox.Text" Value="Wide Visual State" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<controls:PageHeader x:Name="pageHeader"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignRightWithPanel="True"
RelativePanel.AlignTopWithPanel="True"
Text="Main Page">
<!-- secondary commands -->
<controls:PageHeader.SecondaryCommands>
<AppBarButton Click="{x:Bind ViewModel.GotoSettings}" Label="Settings" />
<AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" />
<AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" />
</controls:PageHeader.SecondaryCommands>
</controls:PageHeader>
<RelativePanel EntranceNavigationTransitionInfo.IsTargetElement="True"
RelativePanel.AlignBottomWithPanel="True"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignRightWithPanel="True"
RelativePanel.Below="pageHeader">
<controls:Resizer x:Name="parameterResizer" Margin="16,16,16,0">
<TextBox MinWidth="150"
MinHeight="62"
Header="Parameter to pass"
Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap">
<Interactivity:Interaction.Behaviors>
<!-- enable submit on enter key -->
<Behaviors:KeyBehavior Key="Enter">
<Core:CallMethodAction MethodName="GotoDetailsPage" TargetObject="{Binding}" />
</Behaviors:KeyBehavior>
<!-- focus on textbox when page loads -->
<Core:EventTriggerBehavior>
<Behaviors:FocusAction />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</TextBox>
</controls:Resizer>
<Button x:Name="submitButton"
Click="{x:Bind ViewModel.GotoDetailsPage}"
Content="Submit"
RelativePanel.AlignBottomWith="parameterResizer"
RelativePanel.RightOf="parameterResizer" />
<TextBlock x:Name="stateTextBox"
Margin="16,16,0,0"
RelativePanel.AlignLeftWith="parameterResizer"
RelativePanel.Below="parameterResizer"
Text="Current Visual State" />
<!-- content -->
<!-- content -->
<Button x:Name="loadButton"
HorizontalAlignment="Center"
Width="180"
Height="50"
RelativePanel.Below="stateTextBox"
Click="{x:Bind ViewModel.QRCodeCick}">
<StackPanel Orientation="Horizontal">
<SymbolIcon Width="48"
VerticalAlignment="Center"
Height="48"
Symbol="Camera" />
<TextBlock Margin="12,0,0,0"
VerticalAlignment="Center"
Text="Read QR Code" />
</StackPanel>
</Button>
<TextBox x:Name="QRTextBox"
PlaceholderText="Enter Code"
Text="{x:Bind ViewModel.QRText,Mode=TwoWay}"
RelativePanel.Below="loadButton"
Margin="0,0,0,12"/>
</RelativePanel>
</RelativePanel>
</Page>
MainPage.xaml.cs
using System;
using WindowsApp3.ViewModels;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using System.Collections.ObjectModel;
namespace WindowsApp3.Views
{
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;
}
}
}
MagePageViewModel.cs
using Template10.Mvvm;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Threading.Tasks;
using Template10.Services.NavigationService;
using Windows.UI.Xaml.Navigation;
using ZXing.Mobile;
namespace WindowsApp3.ViewModels
{
public class MainPageViewModel : ViewModelBase
{
MobileBarcodeScanner scanner;
public MainPageViewModel()
{
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
Value = "Designtime value";
}
//Create a new instance of our scanner
scanner = new MobileBarcodeScanner();
// this.Dispatcher
// scanner.Dispatcher = this.Dispatcher;
}
string _Value = "Gas";
public string Value { get { return _Value; } set { Set(ref _Value, value); } }
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
{
if (suspensionState.Any())
{
Value = suspensionState[nameof(Value)]?.ToString();
}
await Task.CompletedTask;
}
public override async Task OnNavigatedFromAsync(IDictionary<string, object> suspensionState, bool suspending)
{
if (suspending)
{
suspensionState[nameof(Value)] = Value;
}
await Task.CompletedTask;
}
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
{
args.Cancel = false;
await Task.CompletedTask;
}
public void GotoDetailsPage() =>
NavigationService.Navigate(typeof(Views.DetailPage), Value);
public void GotoSettings() =>
NavigationService.Navigate(typeof(Views.SettingsPage), 0);
public void GotoPrivacy() =>
NavigationService.Navigate(typeof(Views.SettingsPage), 1);
public void GotoAbout() =>
NavigationService.Navigate(typeof(Views.SettingsPage), 2);
public async void QRCodeCick()
{
scanner.UseCustomOverlay = false;
scanner.TopText = "Hold camera up to barcode";
scanner.BottomText = "Camera will automatically scan barcode\r\n\r\nPress the 'Back' button to Cancel";
//Start scanning
var result = await scanner.Scan();
if (result != null)
_QRText = result.Text;
}
string _QRText;
public string QRText { get { return _QRText; } set { Set(ref _QRText, value); } }
}
}
Details.xaml
<Page x:Class="WindowsApp3.Views.DetailPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Behaviors="using:Template10.Behaviors"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:controls="using:Template10.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:WindowsApp3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:WindowsApp3.ViewModels"
x:Name="ThisPage"
mc:Ignorable="d">
<Page.DataContext>
<vm:DetailPageViewModel x:Name="ViewModel" />
</Page.DataContext>
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
<VisualState x:Name="VisualStateNarrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<!-- TODO: change properties for narrow view -->
</VisualState.Setters>
</VisualState>
<VisualState x:Name="VisualStateNormal">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<!-- TODO: change properties for normal view -->
</VisualState.Setters>
</VisualState>
<VisualState x:Name="VisualStateWide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<!-- TODO: change properties for wide view -->
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- header -->
<controls:PageHeader x:Name="pageHeader"
Frame="{x:Bind Frame}"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignRightWithPanel="True"
RelativePanel.AlignTopWithPanel="True"
Text="Detail Page" />
<!-- content -->
<ScrollViewer EntranceNavigationTransitionInfo.IsTargetElement="True"
Padding="12,8,0,0"
RelativePanel.AlignBottomWithPanel="True"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignRightWithPanel="True"
RelativePanel.Below="pageHeader"
VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBlock Style="{StaticResource TitleTextBlockStyle}" Text="You passed:" />
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind ViewModel.Value, Mode=OneWay, FallbackValue=DesigntimeValue}" />
<Button x:Name="loadButton"
HorizontalAlignment="Center"
Width="180"
Height="50"
Click="{x:Bind ViewModel.QRCodeCick}">
<StackPanel Orientation="Horizontal">
<SymbolIcon Width="48"
VerticalAlignment="Center"
Height="48"
Symbol="Camera" />
<TextBlock Margin="12,0,0,0"
VerticalAlignment="Center"
Text="Read QR Code" />
</StackPanel>
</Button>
<TextBox x:Name="QRTextBox"
PlaceholderText="Enter Code"
Text="{x:Bind ViewModel.QRText,Mode=TwoWay}"
Margin="0,0,0,12"/>
</StackPanel>
</ScrollViewer>
</RelativePanel>
</Page>
DesignPage.xaml.cs
using WindowsApp3.ViewModels;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Controls;
namespace WindowsApp3.Views
{
public sealed partial class DetailPage : Page
{
public DetailPage()
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Disabled;
}
}
}
DesignViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Template10.Common;
using Template10.Mvvm;
using Template10.Services.NavigationService;
using Windows.UI.Xaml.Navigation;
using ZXing.Mobile;
namespace WindowsApp3.ViewModels
{
public class DetailPageViewModel : ViewModelBase
{
MobileBarcodeScanner scanner;
public DetailPageViewModel()
{
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
Value = "Designtime value";
}
//Create a new instance of our scanner
scanner = new MobileBarcodeScanner();
// this.Dispatcher
// scanner.Dispatcher = this.Dispatcher;
}
private string _Value = "Default";
public string Value { get { return _Value; } set { Set(ref _Value, value); } }
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
{
Value = (suspensionState.ContainsKey(nameof(Value))) ? suspensionState[nameof(Value)]?.ToString() : parameter?.ToString();
await Task.CompletedTask;
}
public override async Task OnNavigatedFromAsync(IDictionary<string, object> suspensionState, bool suspending)
{
if (suspending)
{
suspensionState[nameof(Value)] = Value;
}
await Task.CompletedTask;
}
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
{
args.Cancel = false;
await Task.CompletedTask;
}
public async void QRCodeCick()
{
scanner.UseCustomOverlay = false;
scanner.TopText = "Hold camera up to barcode";
scanner.BottomText = "Camera will automatically scan barcode\r\n\r\nPress the 'Back' button to Cancel";
//Start scanning
var result = await scanner.Scan();
if (result != null)
_QRText = result.Text;
}
string _QRText;
public string QRText { get { return _QRText; } set { Set(ref _QRText, value); } }
}
}
I see two issues with your code:
1.) The _QRText = result.Text does not fire the PropertyChange for the binding.
2.) The main difference is in the pages' NavigationCacheMode property. You are using NavigationCacheMode.Enabled on MainPage and NavigationCacheMode.Disabled for DesignPage.
In the background the ZXing do a page navigation then a back navigation, so when it navigate back to the MainPage it uses the cached version so the ViewModel is cached too.
But in the DesignPage the caching is disabled so the back navigation creates a new page and view model instance so the following code is called in the old view model and not in the new one.
//Start scanning
var result = await scanner.Scan();
if (result != null)
_QRText = result.Text;
So you need to set the NavigationCacheMode property of the page to NavigationCacheMode.Required where you start the QR Code scanning.
I need to make some kind of property "EnableBlinking" for Border element which enables DoubleAnimation for Opacity with interval.
<Border CornerRadius="5" Background="Red" EnableBlinking="True" />
I found example with but as far as I understand this is not supported in Windows Phone. And there is VisualStateManager. Can someone give me any example or good tutorial? I don't understand do I need to create Style, or new element.
Thanks
You can go for advaced XAML concept and can use Attached property with user control.
Create User Control
<UserControl
.................
<UserControl.Resources>
<Storyboard x:Name="storyBoard">
<DoubleAnimation Storyboard.TargetName="brd"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"
RepeatBehavior="Forever"
AutoReverse="True"
Duration="0:0:0.1"/>
</Storyboard>
</UserControl.Resources>
<Grid>
<Border x:Name="brd" CornerRadius="5" Background="Red"/>
</Grid>
</UserControl>
public sealed partial class BorderControl : UserControl
{
public BorderControl()
{
this.InitializeComponent();
}
public static bool GetEnableBlinking(DependencyObject obj)
{
return (bool)obj.GetValue(EnableBlinkingProperty);
}
public static void SetEnableBlinking(DependencyObject obj, bool value)
{
obj.SetValue(EnableBlinkingProperty, value);
}
// Using a DependencyProperty as the backing store for EnableBlinking. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EnableBlinkingProperty =
DependencyProperty.RegisterAttached("EnableBlinking", typeof(bool), typeof(MainPage), new PropertyMetadata(0, (s, e) =>
{
(s as BorderControl).storyBoard.Begin();
}));
}
MainPage.xaml
<Grid>
<local:BorderControl EnableBlinking="true" HorizontalAlignment="Center" VerticalAlignment="Center" Height="100" Width="100"/>
</Grid>
Here is Source Code:
https://onedrive.live.com/?id=58D8C7A3527E52FB%21105&cid=58D8C7A3527E52FB
This is the xaml code what i am using
<GridView
Grid.Row="0"
x:Name="RootGrid"
SelectionMode="None"
IsItemClickEnabled="True"
ItemsSource="{Binding RootListSource}">
<GridView.ItemTemplate>
<DataTemplate>
<UserControl:TreeInfoControl/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
In this my user control, it contain another GridView that holds a different IEnumerable collection. What i am trying to achieve is i need to pass this collection through code. I tried this by adding a dependency property to the treecontrol but it is not working. So i am looking for a solution that enable passing the collection through xaml (somehow through the user control). I know it is possible to add that collection to my existing collection and bind that one. But for now i can't use that method.
Here's how you do it.
Start with your App.xaml so we can reuse the demo template
<Application.Resources>
<DataTemplate x:Key="MyContentControl">
<Grid Height="100" Width="100" Background="Maroon">
<TextBlock Text="{Binding FallbackValue=0}" Foreground="White" FontSize="40" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
</DataTemplate>
</Application.Resources>
Then we can define your user control
<d:UserControl.DataContext>
<local:MyControlViewModel Number="-1" Letter="~K" />
</d:UserControl.DataContext>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left">
<ContentControl Content="{Binding Number}"
ContentTemplate="{StaticResource MyContentControl}" />
<ListView ItemsSource="{Binding Letters}" IsHitTestVisible="False"
ItemTemplate="{StaticResource MyContentControl}"
SelectedItem="{Binding Letter, Mode=TwoWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListView>
</StackPanel>
And then we can define your MainPage.xaml
<Page.DataContext>
<local:MainPageViewModel Letter="C" />
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListView x:Name="MyList" ItemsSource="{Binding Letters}"
ItemTemplate="{StaticResource MyContentControl}"
SelectedItem="{Binding Letter, Mode=TwoWay}" />
<ListView Grid.Column="1" ItemsSource="{Binding Numbers}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<local:MyControlViewModel
x:Key="MyDataContext" Number="{Binding}"
Letters="{Binding ItemsSource, ElementName=MyList}"
Letter="{Binding SelectedItem, ElementName=MyList}" />
</StackPanel.Resources>
<local:MyControl DataContext="{StaticResource MyDataContext}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Nothing special yet, right? Well, not so fast. We're creating the viewmodel for the user control , setting the properties of the view model from the surrounding scope, then passing it in to the DataContext of the user control explicitly. Cool, huh? Simple enough, if you think about it. Want to set those properties inside the tag? Sure you do. But you can't. The order of operation would be all wrong. You'll just have to trust me.
Now, there's ZERO code behind for your user control. But the view model looks like this:
public class MyControlViewModel : BindableBase
{
public int Number
{
get { return (int)GetValue(NumberProperty); }
set
{
SetValue(NumberProperty, value);
base.RaisePropertyChanged();
}
}
public static readonly DependencyProperty NumberProperty =
DependencyProperty.Register("Number", typeof(int), typeof(MyControlViewModel),
new PropertyMetadata(0, (s, e) => { }));
public string Letter
{
get { return (string)GetValue(LetterProperty); }
set
{
SetValue(LetterProperty, value);
base.RaisePropertyChanged();
}
}
public static readonly DependencyProperty LetterProperty =
DependencyProperty.Register("Letter", typeof(string), typeof(MyControlViewModel),
new PropertyMetadata("Z", (s, e) => { }));
public ObservableCollection<string> Letters
{
get { return (ObservableCollection<string>)GetValue(LettersProperty); }
set
{
SetValue(LettersProperty, value);
base.RaisePropertyChanged();
}
}
public static readonly DependencyProperty LettersProperty =
DependencyProperty.Register("Letters", typeof(ObservableCollection<string>),
typeof(MyControlViewModel),
new PropertyMetadata(new ObservableCollection<string>(new[] { "~W", "~X", "~Y", "~Z" }), (s, e) => { }));
}
All the properties are dependency properties. I hope you noticed. I didn't just do that because I like to type. Though I do like to type. Fact is, I did that because in order to have internal binding you must use a dependency property - and a dependency property that raises property changed! That last part isn't trivial. But does it have to be in a view model? No. But I like it that way.
You might reference this: http://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html
There's also no code behind for your MainPage. But the view model looks like this:
public class MainPageViewModel : BindableBase
{
public MainPageViewModel()
{
this._Letters = new ObservableCollection<string>(new[] { "A", "B", "C", "D" });
this._Numbers = new ObservableCollection<int>(new[] { 1, 2, 3, 4 });
}
public string Letter
{
get { return (string)GetValue(LetterProperty); }
set
{
SetValue(LetterProperty, value);
base.RaisePropertyChanged();
}
}
public static readonly DependencyProperty LetterProperty =
DependencyProperty.Register("Letter", typeof(string), typeof(MyControlViewModel),
new PropertyMetadata("Z", (s, e) => { }));
ObservableCollection<string> _Letters = new ObservableCollection<string>();
public ObservableCollection<string> Letters { get { return _Letters; } }
ObservableCollection<int> _Numbers = new ObservableCollection<int>();
public ObservableCollection<int> Numbers { get { return _Numbers; } }
}
The bindable base is standard, here's the code for it:
public abstract class BindableBase : DependencyObject, System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (!object.Equals(storage, value))
{
storage = value;
if (PropertyChanged != null)
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
When it's all done, you should get exactly what you want. Something like this:
Not to over-simplify things. But, it's that easy.
Look, getting your head wrapped around XAML is not always easy when you start to nest contexts. I don't blame you for not getting it on first run. But I hope this helps you get started. Keep pushing
Best of luck!