locked
SketchFlow: Remember the state of a Checkbox between screens RRS feed

  • Question

  • Hello,

    I'm quite new to programming in general, so maybe this problem is trivial :o)

    For a university project I am currently programming a SketchFlow Prototype which I will not explain in detail.

    One of the problems I fight with right now is:


    - I have two screens

    Screen 1) A list that shows elements with Checkboxes that define wether the element is active, on startup the Checkbox is not ckecked.
    This is not a List in the sense of ListBox, just a Checkbox followed by a TextBlock

    Screen 2) A detail-page for element 1 (the rest of the elements in screen 1 are neglected right now)
    Screen 2) has One button "Activate" and one button "Cancel"

    - by clicking on "Activate" I want to jump to Screen 1) and the Checkbox should be checked
    - by clicking "Cancel" I want to jump to Screen 1) and the Checkbox remains in the state is was before (checked OR unchecked)


    I suppose this is quite easy but I have difficulties finding a good description how to do this (there are several threads that deal with similar problems, but they are old and the links they provide don't work anymore)

    I tried to get some information on data-binding, but this topic is very complex and I'm not sure where to begin (and I don't know if it's neccessary for my problem)

    Hopefully someone can help me with this.


    Cheers Chris


    • Edited by Chris-o-Mio Tuesday, September 25, 2012 11:08 AM added some words that got lost before
    Tuesday, September 25, 2012 10:22 AM

Answers

  • The easiest way for me is to declare some sort of value in your App.xaml file that can be accessed by both screens. I should point out that this is generally not how you would create the data structure for a large project, but it certainly gets the job done for a prototype. Here's an example:

    MyData.cs (inside the SliverlightPrototype1 project, contains a boolean that I'll reference from both screens):

    namespace SilverlightPrototype1
    {
    	public class MyData
    	{
    		public bool BoolValue { get; set; }
    	}
    }

    App.xaml (inside the SliverlightPrototype1 project, declares an instance of MyData that will be available to all screens):

    <Application
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:local="clr-namespace:SilverlightPrototype1"
    	x:Class="SilverlightPrototype1.App">
    	<Application.Resources>
    		<!-- Resources scoped at the Application level should be defined here. -->
    		<ResourceDictionary>
    			<ResourceDictionary.MergedDictionaries>
    				<ResourceDictionary Source="/Microsoft.Expression.Prototyping.SketchControls;component/ScrollViewerStyles.xaml"/>
    				<ResourceDictionary Source="/SilverlightPrototype1.Screens;component/SketchStyles.xaml"/>
    			</ResourceDictionary.MergedDictionaries>
    
    			<local:MyData x:Key="MyDataInstance" />
    		</ResourceDictionary>
    	</Application.Resources>
    </Application>
    

    Screen_1.xaml (inside the SliverlightPrototype1Screens project):

    <UserControl
    	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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:pi="http://schemas.microsoft.com/prototyping/2010/interactivity"
    	mc:Ignorable="d"
    	x:Class="SilverlightPrototype1Screens.Screen_1"
    	Width="640" Height="480">
    
    	<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource MyDataInstance}">
    		<CheckBox Content="CheckBox" HorizontalAlignment="Left" Style="{StaticResource CheckBox-Sketch}" VerticalAlignment="Top" Margin="74,99,0,0" IsChecked="{Binding BoolValue, Mode=TwoWay}"/>
    		<Button Content="Go to Screen 2" HorizontalAlignment="Left" Style="{StaticResource Button-Sketch}" VerticalAlignment="Top" Width="137" Margin="74,139,0,0">
    			<i:Interaction.Triggers>
    				<i:EventTrigger EventName="Click">
    					<pi:NavigateToScreenAction TargetScreen="SilverlightPrototype1Screens.Screen_2"/>
    				</i:EventTrigger>
    			</i:Interaction.Triggers>
    		</Button>
    	</Grid>
    </UserControl>

    Screen_2.xaml:

    <UserControl
    	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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:pi="http://schemas.microsoft.com/prototyping/2010/interactivity"
    	mc:Ignorable="d"
    	x:Class="SilverlightPrototype1Screens.Screen_2"
    	Width="640" Height="480">
    
    	<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource MyDataInstance}">
    		<CheckBox Content="From Screen 1" HorizontalAlignment="Left" Style="{StaticResource CheckBox-Sketch}" VerticalAlignment="Top" Margin="86,94,0,0" Width="149" IsChecked="{Binding BoolValue, Mode=TwoWay}"/>
    		<Button Content="Back to Screen 1" HorizontalAlignment="Left" Style="{StaticResource Button-Sketch}" VerticalAlignment="Top" Width="139" Margin="86,133,0,0">
    			<i:Interaction.Triggers>
    				<i:EventTrigger EventName="Click">
    					<pi:NavigateToScreenAction TargetScreen="SilverlightPrototype1Screens.Screen_1"/>
    				</i:EventTrigger>
    			</i:Interaction.Triggers>
    		</Button>
    	</Grid>
    </UserControl>

    The trick to all of this is, of course, data binding. The declaration in App.xaml creates a "Local Resource". In Screen_1, by selecting the LayoutRoot, selecting the marker next to the DataContext property (the little square thing), and then selecting "LocalResource -> MyDataInstance", we make the LayoutRoot reference the MyData instance we created in App.xaml. From there, we can bind the IsChecked to the boolean value. We have a two-way binding set up, which means whenever we change this checkbox, it will change the value in MyDataInstance.

    We then set up the Screen 2 binding the same way, so anytime we change the checkbox in Screen 2, the checkbox on Screen 1 would also change. That way, when we navigate back, everything's synchronized. Data binding is nifty, but it's tricky to wrap your head around at first.

    I should point out this doesn't exactly match your description, it's just a quick data binding example. I would probably change Screen 2 so it doesn't have a checkbox (or maybe a disabled one so the user can't interact with it). Your "Accept" and "Cancel" buttons would probably be programmatically handled to set the value on MyData (accessible by casting the DataContext of your LayoutRoot -- or whatever you're using -- to a MyData instance) and then navigate back to Screen 1 on their own (as opposed to using a NavigateToScreenAction that operates automatically on button click).

    Hope that helps point you in the right direction.

    • Marked as answer by Chris-o-Mio Tuesday, September 25, 2012 2:52 PM
    Tuesday, September 25, 2012 1:33 PM

All replies

  • The easiest way for me is to declare some sort of value in your App.xaml file that can be accessed by both screens. I should point out that this is generally not how you would create the data structure for a large project, but it certainly gets the job done for a prototype. Here's an example:

    MyData.cs (inside the SliverlightPrototype1 project, contains a boolean that I'll reference from both screens):

    namespace SilverlightPrototype1
    {
    	public class MyData
    	{
    		public bool BoolValue { get; set; }
    	}
    }

    App.xaml (inside the SliverlightPrototype1 project, declares an instance of MyData that will be available to all screens):

    <Application
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:local="clr-namespace:SilverlightPrototype1"
    	x:Class="SilverlightPrototype1.App">
    	<Application.Resources>
    		<!-- Resources scoped at the Application level should be defined here. -->
    		<ResourceDictionary>
    			<ResourceDictionary.MergedDictionaries>
    				<ResourceDictionary Source="/Microsoft.Expression.Prototyping.SketchControls;component/ScrollViewerStyles.xaml"/>
    				<ResourceDictionary Source="/SilverlightPrototype1.Screens;component/SketchStyles.xaml"/>
    			</ResourceDictionary.MergedDictionaries>
    
    			<local:MyData x:Key="MyDataInstance" />
    		</ResourceDictionary>
    	</Application.Resources>
    </Application>
    

    Screen_1.xaml (inside the SliverlightPrototype1Screens project):

    <UserControl
    	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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:pi="http://schemas.microsoft.com/prototyping/2010/interactivity"
    	mc:Ignorable="d"
    	x:Class="SilverlightPrototype1Screens.Screen_1"
    	Width="640" Height="480">
    
    	<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource MyDataInstance}">
    		<CheckBox Content="CheckBox" HorizontalAlignment="Left" Style="{StaticResource CheckBox-Sketch}" VerticalAlignment="Top" Margin="74,99,0,0" IsChecked="{Binding BoolValue, Mode=TwoWay}"/>
    		<Button Content="Go to Screen 2" HorizontalAlignment="Left" Style="{StaticResource Button-Sketch}" VerticalAlignment="Top" Width="137" Margin="74,139,0,0">
    			<i:Interaction.Triggers>
    				<i:EventTrigger EventName="Click">
    					<pi:NavigateToScreenAction TargetScreen="SilverlightPrototype1Screens.Screen_2"/>
    				</i:EventTrigger>
    			</i:Interaction.Triggers>
    		</Button>
    	</Grid>
    </UserControl>

    Screen_2.xaml:

    <UserControl
    	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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:pi="http://schemas.microsoft.com/prototyping/2010/interactivity"
    	mc:Ignorable="d"
    	x:Class="SilverlightPrototype1Screens.Screen_2"
    	Width="640" Height="480">
    
    	<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource MyDataInstance}">
    		<CheckBox Content="From Screen 1" HorizontalAlignment="Left" Style="{StaticResource CheckBox-Sketch}" VerticalAlignment="Top" Margin="86,94,0,0" Width="149" IsChecked="{Binding BoolValue, Mode=TwoWay}"/>
    		<Button Content="Back to Screen 1" HorizontalAlignment="Left" Style="{StaticResource Button-Sketch}" VerticalAlignment="Top" Width="139" Margin="86,133,0,0">
    			<i:Interaction.Triggers>
    				<i:EventTrigger EventName="Click">
    					<pi:NavigateToScreenAction TargetScreen="SilverlightPrototype1Screens.Screen_1"/>
    				</i:EventTrigger>
    			</i:Interaction.Triggers>
    		</Button>
    	</Grid>
    </UserControl>

    The trick to all of this is, of course, data binding. The declaration in App.xaml creates a "Local Resource". In Screen_1, by selecting the LayoutRoot, selecting the marker next to the DataContext property (the little square thing), and then selecting "LocalResource -> MyDataInstance", we make the LayoutRoot reference the MyData instance we created in App.xaml. From there, we can bind the IsChecked to the boolean value. We have a two-way binding set up, which means whenever we change this checkbox, it will change the value in MyDataInstance.

    We then set up the Screen 2 binding the same way, so anytime we change the checkbox in Screen 2, the checkbox on Screen 1 would also change. That way, when we navigate back, everything's synchronized. Data binding is nifty, but it's tricky to wrap your head around at first.

    I should point out this doesn't exactly match your description, it's just a quick data binding example. I would probably change Screen 2 so it doesn't have a checkbox (or maybe a disabled one so the user can't interact with it). Your "Accept" and "Cancel" buttons would probably be programmatically handled to set the value on MyData (accessible by casting the DataContext of your LayoutRoot -- or whatever you're using -- to a MyData instance) and then navigate back to Screen 1 on their own (as opposed to using a NavigateToScreenAction that operates automatically on button click).

    Hope that helps point you in the right direction.

    • Marked as answer by Chris-o-Mio Tuesday, September 25, 2012 2:52 PM
    Tuesday, September 25, 2012 1:33 PM
  • (...)

    MyData.cs (inside the SliverlightPrototype1 project, contains a boolean that I'll reference from both screens):

    namespace SilverlightPrototype1
    {
    	public class MyData
    	{
    		public bool BoolValue { get; set; }
    	}
    }

    (...)

    Hello Mr. Hilstrom,

    thanks a lot for the quick reply. I tried to use the code you provided, but it says:

    The tag "MyData" does not exist in XML namespace"clr-namespace:SilverlightPrototype1".

    I'm not entirely sure if I used the component MyData.cs correctly...

    I did this: New Element: Class  -  Name: MyData.cs

    was this correct?

    Thanks for offering your time, this is very helpful indeed!

    Cheers Chris

    Tuesday, September 25, 2012 2:25 PM
  • Yes, that was correct. However, I doubt your project is called "SilverlightPrototype1". By default, the project name is used as the namespace for classes created in the root folder of the project (hence my "MyData" example above being in namespace "SilverlightPrototype1"). You'll have to change the "local" namespace in the App.xaml file to match the namespace of your MyData class.

    For example, if my project was called "TestProject", the line in App.xaml would look like this:

    xmlns:local="clr-namespace:TestProject"
    

    Hope that helps.

    Tuesday, September 25, 2012 2:29 PM
  • My project was indeed called "SilverlightPrototype1" - I thought this is easier than renaming it throughout the document (since I don't know, were the name could possibly be referenced)

    Funny, it does not work, when I insert a new element: Class: MyData.cs

    I did (right before i saw your answer) delete the class MyData.cs and added it in app.xaml.cs as a new namespace and THAN it worked :D

    So thanks again, this was VERY helpful, I can't believe how much time I wasted trying to figure it out myself, when all I had to do is ask it here...

    However, a lot of work is still due - I hope I understood the underlying concept enough to come up with the rest of my work, otherwise I may get back to this post.

    Cheers Chris

    Tuesday, September 25, 2012 2:52 PM