WPF Multi-touch tracking touch points - wpf

I'm trying to do a simple application that, when a user touchs a screen, app creates simple point, ellipse, or sth 2d object, and when user moves his finger it should follow, but also when there is a scond touch at the same time new object also has to be created and do the same thing with respect to users movement. Whenever user fingersup, object will be deleted.
To do this, I'm trying to change the touchdrawing code from this link http://www.cookingwithxaml.com/recipes/wpf4/wpf4touch.zip but I couldn't figure out which method should I need to change ?
Can you give advice about that please ?
Thanks.

Here is some sample xaml/C# code that does what I think you want:
MainWindow.xaml:
<Window x:Class="MultitouchExperiments.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas
x:Name="TouchCanvas"
TouchDown="TouchCanvas_TouchDown" TouchUp="TouchCanvas_TouchUp"
TouchMove="TouchCanvas_TouchMove" TouchLeave="TouchCanvas_TouchLeave"
TouchEnter="TouchCanvas_TouchEnter"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Background="Black"
IsManipulationEnabled="True" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
namespace MultitouchExperiments
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Dictionary<TouchDevice, Ellipse> _Followers = new Dictionary<TouchDevice, Ellipse>();
public MainWindow()
{
InitializeComponent();
}
private void TouchCanvas_TouchDown(object sender, TouchEventArgs e)
{
TouchCanvas.CaptureTouch(e.TouchDevice);
Ellipse follower = new Ellipse();
follower.Width = follower.Height = 50;
follower.Fill = Brushes.White;
follower.Stroke = Brushes.White;
TouchPoint point = e.GetTouchPoint(TouchCanvas);
follower.RenderTransform = new TranslateTransform(point.Position.X, point.Position.Y);
_Followers[e.TouchDevice] = follower;
TouchCanvas.Children.Add(follower);
}
private void TouchCanvas_TouchUp(object sender, TouchEventArgs e)
{
TouchCanvas.ReleaseTouchCapture(e.TouchDevice);
TouchCanvas.Children.Remove(_Followers[e.TouchDevice]);
_Followers.Remove(e.TouchDevice);
}
private void TouchCanvas_TouchMove(object sender, TouchEventArgs e)
{
if (e.TouchDevice.Captured == TouchCanvas)
{
Ellipse follower = _Followers[e.TouchDevice];
TranslateTransform transform = follower.RenderTransform as TranslateTransform;
TouchPoint point = e.GetTouchPoint(TouchCanvas);
transform.X = point.Position.X;
transform.Y = point.Position.Y;
}
}
private void TouchCanvas_TouchLeave(object sender, TouchEventArgs e)
{
//Debug.WriteLine("leave " + e.TouchDevice.Id);
}
private void TouchCanvas_TouchEnter(object sender, TouchEventArgs e)
{
//Debug.WriteLine("enter " + e.TouchDevice.Id);
}
}
}

Related

Scroll multiple image in wpf

<Grid>
<Grid.Background>
<ImageBrush ImageSource="Images\Desert.jpg"
Stretch="UniformToFill" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0,0,1024,768"/>
</Grid.Background>
<Grid.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<RectAnimation Storyboard.TargetProperty="Background.Viewport"
To="-1024,0,1024,768" Duration="0:0:10"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>
I have this code that scroll a single image in a grid with looping.
Now I have 2 images 1(red) and 2(yellow) am looking something like this.
and it will scroll in loop
You can build a single ImageSource based on multiple images if you wanted to follow your current approach. I have 2 png's (Desert1.png and Desert2.png in an Images folder) and use DataBinding to set the ImageBrush ImageSource to a property defined on the code behind:
<!- Your original xaml ... only difference is the binding -->
<ImageBrush ImageSource="{Binding CombinedImage}"
Stretch="None" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0,0,1024,768"/>
Here's a sample of the code behind (feel free to refactor / use / abuse as you see fit):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xaml;
namespace WpfApplication
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var uriSource1 = new Uri(#"pack://application:,,,/Images/Desert1.png", UriKind.Absolute);
BitmapImage bitmapImage1 = new BitmapImage(uriSource1);
var uriSource2 = new Uri(#"pack://application:,,,/Images/Desert2.png", UriKind.Absolute);
BitmapImage bitmapImage2 = new BitmapImage(uriSource2);
this.DataContext = this;
List<BitmapImage> images = new List<BitmapImage>() { bitmapImage1, bitmapImage2 };
CombinedImage = GetCombinedImage(images);
}
private static RenderTargetBitmap GetCombinedImage(IEnumerable<BitmapImage> images )
{
// Get total width of all images
int totalWidthOfAllImages = images.Sum(p => (int)p.Width);
// Get max height of all images
int maxHeightOfAllImages = images.Max(p => (int)p.Height);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
double left = 0;
foreach (BitmapImage image in images)
{
drawingContext.DrawImage(image, new Rect(left, 0, image.Width, image.Height));
left += image.Width;
}
}
RenderTargetBitmap bmp = new RenderTargetBitmap(totalWidthOfAllImages, maxHeightOfAllImages, 96, 96, PixelFormats.Default);
bmp.Render(drawingVisual);
return bmp;
}
public ImageSource CombinedImage { get; private set; }
}
}
I have code for image slider. I created using user control for windows phone
Please check with this video http://www.screencast.com/t/XnhHwQFY For first time you need to change logic.
But, I think same code you can use for WPF also
ImageSlider.xaml - Create user control
<UserControl x:Class="ImageSliderDemo.ImageSlider"
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"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<Canvas Height="220" x:Name="imageSliderCanvas" Width="451">
<Image x:Name="imageViewOne"
Canvas.Left="0"
Canvas.Top="0"
Height="220" Width="440" Canvas.ZIndex="9">
<Image.RenderTransform>
<TranslateTransform />
</Image.RenderTransform>
</Image>
<Image x:Name="imageViewTwo"
Canvas.Left="0"
Height="220" Width="440" Canvas.ZIndex="10">
<Image.RenderTransform>
<TranslateTransform />
</Image.RenderTransform>
</Image>
</Canvas>
<StackPanel x:Name="PART_Host" />
</Grid>
</UserControl>
ImageSlider.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Threading;
using System.Windows.Media.Imaging;
using System.Windows.Markup;
namespace ImageSliderDemo
{
public partial class ImageSlider : UserControl
{
private const int LOWER_ZINDEX = 9, UPPER_ZINDEX = 11, POSITION_FROM480 = 480, POSITION_TO0 = 0;
private int nextImage = 1;
#region "Image Slider Properies"
#region "Property - Length Readonly"
public static readonly DependencyProperty LengthProperty = DependencyProperty.Register("Length", typeof(int), typeof(ImageSlider), new PropertyMetadata(0));
public int Length
{
get { return (int)GetValue(LengthProperty); }
private set { SetValue(LengthProperty, value); }
}
#endregion
#region "Property - Begin Delay Readonly"
public static readonly DependencyProperty BeginDelayProperty = DependencyProperty.Register("BeginDelay", typeof(double), typeof(ImageSlider), new PropertyMetadata(5000.00));
public double BeginDelay
{
get { return (double)GetValue(BeginDelayProperty); }
set { SetValue(BeginDelayProperty, value); }
}
#endregion
#region "Property - Animation Duration Readonly"
public static readonly DependencyProperty AnimationDurationProperty = DependencyProperty.Register("AnimationDuration", typeof(double), typeof(ImageSlider), new PropertyMetadata(900.00));
public double AnimationDuration
{
get { return (double)GetValue(AnimationDurationProperty); }
set { SetValue(AnimationDurationProperty, value); }
}
#endregion
#region "Property - Images"
public static readonly DependencyProperty ImagesProperty = DependencyProperty.Register("Images", typeof(List<SliderImage>), typeof(ImageSlider), new PropertyMetadata(null));
public List<SliderImage> Images
{
get { return (List<SliderImage>)GetValue(ImagesProperty); }
set { SetValue(ImagesProperty, value); }
}
#endregion
#endregion
public ImageSlider()
{
InitializeComponent();
}
/// <summary>
/// This Start method used begin the animation
/// </summary>
public void Start()
{
if (this.Images != null)
{
this.Length = this.Images.Count;
hidePrevious(imageViewOne);
showNext(imageViewTwo);
}
else
{
MessageBox.Show("Please add atleast two images");
}
}
#region "Animation methods"
private void showNext(Image imageView)
{
TranslateTransform trans = imageView.RenderTransform as TranslateTransform;
DoubleAnimation animation = new DoubleAnimation();
animation.To = POSITION_TO0;
animation.Duration = TimeSpan.FromMilliseconds(this.AnimationDuration);
animation.From = POSITION_FROM480;
animation.BeginTime = TimeSpan.FromMilliseconds(this.BeginDelay);
Storyboard.SetTarget(animation, trans);
Storyboard.SetTargetProperty(animation, new
PropertyPath(TranslateTransform.XProperty));
// Create storyboard, add animation, and fire it up!
Storyboard storyboard = new Storyboard();
storyboard.Children.Add(animation);
storyboard.Begin();
Canvas.SetZIndex(imageView, UPPER_ZINDEX);
imageView.Visibility = Visibility.Visible;
if (nextImage > this.Length)
{
nextImage = 1;
}
BitmapImage nextBitmapImage = new BitmapImage(new Uri(this.Images[nextImage-1].Path, UriKind.Relative));
imageView.Source = nextBitmapImage;
nextImage++;
}
private void hidePrevious(Image imageView)
{
TranslateTransform trans = imageView.RenderTransform as TranslateTransform;
DoubleAnimation animation = new DoubleAnimation();
animation.To = - POSITION_FROM480;
animation.Duration = TimeSpan.FromMilliseconds(this.AnimationDuration);
animation.From = POSITION_TO0;
animation.BeginTime = TimeSpan.FromMilliseconds(this.BeginDelay);
Storyboard.SetTarget(animation, trans);
Storyboard.SetTargetProperty(animation, new
PropertyPath(TranslateTransform.XProperty));
// Create storyboard, add animation, and fire it up!
Storyboard storyboard = new Storyboard();
storyboard.Children.Add(animation);
storyboard.Begin();
animation.Completed += hideAnimation_Completed;
}
private void hideAnimation_Completed(object sender, EventArgs e)
{
if (Canvas.GetZIndex(imageViewOne) > Canvas.GetZIndex(imageViewTwo))
{
Canvas.SetZIndex(imageViewOne, LOWER_ZINDEX);
hidePrevious(imageViewOne);
showNext(imageViewTwo);
}
else
{
Canvas.SetZIndex(imageViewTwo, LOWER_ZINDEX);
hidePrevious(imageViewTwo);
showNext(imageViewOne);
}
}
#endregion
}
}
Ctrl + B , Just build once
SliderImage.cs -- Add new class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ImageSliderDemo
{
public class SliderImage
{
public string Name { get; set; }
public string Path { get; set; }
public SliderImage(string name, string path)
{
this.Name = name;
this.Path = path;
}
}
}
then do this steps
MainPage.xaml
add at top of xaml page xmlns:local="clr-namespace:[YOUR_PROJECT_NAMESPACE]"
then add just add this below in xaml
<local:ImageSlider x:Name="imageSlider"/>
MainPage.xaml.cs load images
List<SliderImage> images = new List<SliderImage>();
images.Add(new SliderImage("One", "Images/1.png"));
images.Add(new SliderImage("Two", "Images/2.png"));
images.Add(new SliderImage("Three", "Images/3.png"));
images.Add(new SliderImage("Four", "Images/4.png"));
imageSlider.Images = images;
imageSlider.Start();
Note : I used ImageSliderDemo as my namespace. If your using different please make sure you updated in user control as well. And I found only first time it is show same image twice. you can change the logic in ImageSlider.xaml.cs file

DataContext in Window_Loaded

I'm very new to WPF. I'm currently doing a code to detect joints coordinate using the Kinect SDK and displaying on a simple textbox in WPF. The code to detect joints are in a private void Window_Loaded(object sender, RoutedEventArgs e) method. To display the coordinates, I used DataContext. Without further ado, let's just see the XAML code:
<Window x:Class="Prototype.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="480" Width="640">
<Grid>
<TextBox x:Name="coordinateText" Width="150" Height="20" Margin="441,409,27,12" Text="{Binding Path=xInfo}"/>
</Grid>
And this is my C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Research.Kinect.Nui;
using Coding4Fun.Kinect.Wpf;
namespace Prototype
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//this.DataContext = new Coordinate { xInfo = "5" };
}
Runtime nui = new Runtime();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new Coordinate { xInfo = "5" };
nui.Initialize(RuntimeOptions.UseSkeletalTracking); //code for detecting joints
//some code for detecting joints
}
public class Coordinate
{
public string xInfo { get; set; }
public string yInfo { get; set; }
public string zInfo { get; set; }
}
}
}
The thing is the information will not be loaded in the textbox if this.DataContext = new Coordinate { xInfo = "5" }; is not placed in the MainWindow. I have to put it in the Window_Loaded method. Any solutions?
As Coder323 said When window is loaded you need tell WPF TextBox that the Value of the variable xInfo is changed so you should use INotifyPropertyChanged in your Model Class
then where ever you change theValue of your Object it will pick up the changed Value... also
Just Set the DataContext=myCordinate in the Window Constructor then, make my cordinate a variable in the window class.
public class Coordinate : INotifyPropertyChanged
{
private string xInfo;
public string XInfo {
get{retun value};
set{
xInfo=Value;
FirePropertyChanged("XInfo")
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void FirePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Do this for other properties and now you can set the value of myCordinate.XInfo="what ever you like" in any event it will notify to your view that the respective property has changed..
I am putting my complete solution here
My Coordinate class
public class Coordinates : INotifyPropertyChanged
{
private string xInfo;
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public string XInfo
{
get { return xInfo; }
set
{
xInfo = value;
InvokePropertyChanged(new PropertyChangedEventArgs("XInfo"));
}
}
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
#endregion
}
My Xaml
<Window x:Class="TestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<TextBox Text="{Binding Path=XInfo}" Height="30" Widht="100"></TextBox>
</Grid>
My Xaml.cs
namespace TestApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Coordinates myCoordinates;
public MainWindow()
{
myCoordinates=new Coordinates();
this.DataContext = myCoordinates;
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
myCoordinates.XInfo = "Acbd";
}
}
}
And yes this test Project i made... is working
This might Help :)

How can i get startX and startY position?

How can i get the startX and startY position of the rectToGetXAndY. This piece of functionality is very critical to my application but it is driving me crazy. The only approach that comes to my mind is to ask the user to manually click on the top left border of the grid and then handle mouseleftbuttondown event. Obviously this is not the solution i want. Here is my code :-
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="DelSilverlightApp.MainPage"
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"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="800">
<Grid x:Name="LayoutRoot" Background="DarkSlateGray">
<Grid x:Name="rectToGetXAndY" Background="HotPink" Width="300" Height="300" HorizontalAlignment="Center" VerticalAlignment="Center">
</Grid>
</Grid>
</UserControl>
EDIT :-
This the code behind :-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace DelSilverlightApp
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
GeneralTransform gt = rectToGetXAndY.TransformToVisual(null);
Point p = gt.Transform(new Point(0, 0));
MessageBox.Show(p.X + " " + p.Y);
}
}
}
Thanks in advance :)
I made it work using #AnthonyWJones' code using the following:
XAML
<UserControl x:Class="GetPositionUi.MainPage"
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"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="DarkSlateGray">
<Grid x:Name="rectToGetXAndY"
Background="HotPink"
Width="300"
Height="300"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock x:Name="PositionTextBlock" Text="{Binding Path=ReferencePosition}"/>
</Grid>
</Grid>
</UserControl>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace GetPositionUi
{
public partial class MainPage : UserControl
{
#region ReferencePosition
/// <summary>
/// ReferencePosition Dependency Property
/// </summary>
public static readonly DependencyProperty ReferencePositionProperty =
DependencyProperty.Register("ReferencePosition", typeof(Point), typeof(MainPage),
new PropertyMetadata((Point)(new Point(0, 0)),
new PropertyChangedCallback(OnReferencePositionChanged)));
/// <summary>
/// Gets or sets the ReferencePosition property. This dependency property
/// indicates the reference position of the child element.
/// </summary>
public Point ReferencePosition
{
get { return (Point)GetValue(ReferencePositionProperty); }
set { SetValue(ReferencePositionProperty, value); }
}
/// <summary>
/// Handles changes to the ReferencePosition property.
/// </summary>
private static void OnReferencePositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MainPage)d).OnReferencePositionChanged(e);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes to the ReferencePosition property.
/// </summary>
protected virtual void OnReferencePositionChanged(DependencyPropertyChangedEventArgs e)
{
}
#endregion
public MainPage()
{
InitializeComponent();
}
protected override Size ArrangeOverride(Size finalSize)
{
var arrangedSize = base.ArrangeOverride(finalSize);
GeneralTransform gt = rectToGetXAndY.TransformToVisual(LayoutRoot);
Point p = gt.Transform(new Point(0, 0));
ReferencePosition = p;
return arrangedSize;
}
}
}
The key here is letting the base arrange the controls first, then use the transform to find the position and finally returning the new arrangedSize.
I would not recommend showing a message box at this point, but you can use the dependency property changed callback to do anything you want with the updated position.
In Silveright you can use this code to determine the current X and Y position of rectToGetXAndY relative to LayoutRoot:-
GeneralTransform gt = rectToGetXAndY.TransformToVisual(LayoutRoot);
Point p = gt.Transform(new Point(0, 0));
You can use the VisualTreeHelper...
Vector vector = VisualTreeHelper.GetOffset(rectToGetXAndY);
Point currentPoint = new Point(vector.X, vector.Y);

observable collection not getting updated on UI change

I am trying to bind an observable collection to a user control but it is not getting updated on user change but it is getting updated when the user control is changed through code. Following is an example i tried. It might be a bit long but it is working so you can copy and paste the code as it is.
Please see my question at the end of the post.
--Customer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace TestMVVM
{
class Customer : INotifyPropertyChanged
{
private string firstName;
private string lastName;
public string FirstName
{
get { return firstName; }
set
{
if (firstName != value)
{
firstName = value;
RaisePropertyChanged("FirstName");
}
}
}
public string LastName
{
get { return lastName; }
set
{
if (lastName != value)
{
lastName = value;
RaisePropertyChanged("LastName");
}
}
}
#region PropertChanged Block
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(property));
}
}
#endregion
}
}
--UCTextBox.xaml
<UserControl x:Class="TestMVVM.UCTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="40" Width="200">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="txtTextControl"
VerticalAlignment="Top" Width="120" />
</Grid>
--UCTextBox.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace TestMVVM
{
///
/// Interaction logic for UCTextBox.xaml
///
public partial class UCTextBox : UserControl, INotifyPropertyChanged
{
public UCTextBox()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(UCTextBox),
new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(textChangedCallBack)));
static void textChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
UCTextBox pasTextBox = (UCTextBox)property;
pasTextBox.txtTextControl.Text = (string)args.NewValue;
}
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
NotifyPropertyChanged("Text");
}
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
-- Window1.xaml
<Window x:Class="TestMVVM.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestMVVM"
Title="Window1" Height="300" Width="300">
<Grid>
<local:UCTextBox x:Name="txtUC" />
<Button Height="23" HorizontalAlignment="Left" Margin="39,0,0,82"
Name="btnUpdate" VerticalAlignment="Bottom" Width="75" Click="btnUpdate_Click">Update</Button>
<Button Height="23" Margin="120,0,83,82" Name="btnChange" VerticalAlignment="Bottom" Click="btnChange_Click">Change</Button>
</Grid>
-- Window1.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
namespace TestMVVM
{
///
/// Interaction logic for Window1.xaml
///
public partial class Window1 : Window
{
CustomerHeaderViewModel customerHeaderViewModel = null;
public Window1()
{
InitializeComponent();
customerHeaderViewModel = new CustomerHeaderViewModel();
customerHeaderViewModel.LoadCustomers();
txtUC.DataContext = customerHeaderViewModel.Customers[0];
Binding binding = new Binding();
binding.Source = customerHeaderViewModel.Customers[0];
binding.Path = new System.Windows.PropertyPath("FirstName");
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
txtUC.SetBinding(UCTextBox.TextProperty, binding);
}
private void btnUpdate_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(customerHeaderViewModel.Customers[0].FirstName);
}
private void btnChange_Click(object sender, RoutedEventArgs e)
{
txtUC.Text = "Tom";
}
}
class CustomerHeaderViewModel
{
public ObservableCollection Customers { get; set; }
public void LoadCustomers()
{
ObservableCollection customers = new ObservableCollection();
customers.Add(new Customer { FirstName = "Jim", LastName = "Smith", NumberOfContracts = 23 });
Customers = customers;
}
}
}
When i run the Window1.xaml my user control shows the data as "Jim". Now when i change the text to say "John" and click on Update, the messagebox still shows "Jim" that means the observable collection is not getting updated. When i click on Change button the user control changes the data to "Tom". Now when i click on Update button the Messagebox shows "Tom". Can anyone please tell me how to achieve the updation of observable collection by changing the data in user control rather than through code?
That's because you're not handling the txtTextControl.TextChanged event, so your Text dependency property is never updated.
Anyway, you don't need to handle that manually with a DependencyPropertyChangedCallback and an event handler, you can just bind the txtTextControl.Text to the Text dependency property :
<TextBox Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="txtTextControl"
VerticalAlignment="Top" Width="120"
Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:UCTextBox}}}"/>
An observable collection, observers the collection only. You will get notified when items are added or deleted not when fields of a single items did change. That is something completely different. Like Thomas Levesque said, you just need to bind the right property.

XAML page Flip twist

I have this Page.xaml
<UserControl x:Class="SLBookDemoApp.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SLMitsuControls;assembly=SLMitsuControls"
Width="800" Height="600" Loaded="UserControl_Loaded">
<Grid>
<local:UCBook x:Name="book" Margin="50" />
</Grid>
</UserControl>
And the correspondent Page.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SLMitsuControls;
namespace SLBookDemoApp
{
public partial class Page : UserControl, IDataProvider
{
public Page()
{
InitializeComponent();
}
private List<Grid> pages;
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
/*
pages = new List<Button>
{
new Button { Content = "Page 0"},
new Button { Content = "Page 1", Background = new SolidColorBrush(Colors.Green) },
new Button { Content = "Page 2", Background = new SolidColorBrush(Colors.Yellow) },
new Button { Content = "Page 3", Background = new SolidColorBrush(Colors.Brown) },
new Button { Content = "Page 4", Background = new SolidColorBrush(Colors.Magenta) },
new Button { Content = "Page 5", Background = new SolidColorBrush(Colors.Red) }
};
*/
System.Windows.Application.LoadComponent(this, new System.Uri("/SLBookDemoApp;PagTeste2.xaml", System.UriKind.Relative));
Grid LayoutRoot = ((Grid)(FindName("LayoutRoot")));
//TextBlock testTextBlock = ((TextBlock)(FindName("testTextBlock")));
pages = new List<Grid>
{
};
pages.Add(LayoutRoot);
/*
int i = 0;
foreach (var b in pages)
{
if (i % 2 == 0)
b.Click += Button_Click;
else
b.Click += Button_Click_1;
i++;
}
*/
book.SetData(this);
}
#region IDataProvider Members
public object GetItem(int index)
{
return pages[index];
}
public int GetCount()
{
return pages.Count;
}
#endregion
private void Button_Click(object sender, RoutedEventArgs e)
{
book.AnimateToNextPage(500);
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
book.AnimateToPreviousPage(500);
}
}
}
And the XAML I wnat to include is this PagTeste2.xaml
<Grid
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SLBookDemoApp.PagTeste2"
x:Name="LayoutRoot">
<Rectangle Width="192" Height="80" Fill="#FF8F0A0A" Stroke="#FF000000" Canvas.Left="224" Canvas.Top="104"/>
</Grid>
With the correspondent PagTeste2.xaml.cs
using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
//using System.Windows.Navigation;
using SLMitsuControls;
namespace SLBookDemoApp
{
public partial class PagTeste2
{
public PagTeste2()
{
this.InitializeComponent();
// Insert code required on object creation below this point.
}
}
}
I am getting an error on this line
System.Windows.Application.LoadComponent(this, new System.Uri("/SLBookDemoApp;PagTeste2.xaml", System.UriKind.Relative));
Anyone knows why ?
Use this instead:
this.Content = new PagTeste2();
You only have to do any sort of assembly loading if you're loading content from a different assembly and even then you wouldn't use it to set content.
If you're actually asking how do you dynamically load an assembly, MS have an example of how.
You may want to try /SLBookDemoApp;component/PageTeste2.xaml.
If PagTeste2.xaml is at the top-level folder of your project you can load it using this code:
Application.LoadComponent(
this,
new System.Uri(
"/SLBookDemoApp;component/PagTeste2.xaml",
System.UriKind.Relative
)
);
If you have placed PagTeste2.xaml in a subfolder inside your project (say folder Tests) you need to include the path to the file in the uri:
Application.LoadComponent(
this,
new System.Uri(
"/SLBookDemoApp;component/Tests/PagTeste2.xaml",
System.UriKind.Relative
)
);
Also, pay close attention to spelling. PagTest2.xaml is different from PageTeste2.xaml and PageTest2.xaml. Apparently Test is inserted before the e in Page.
You can read more about pack URI's on MSDN.

Resources