I'm trying to implement a real-time plot UI, i'm using WPF with the MVVM Pattern and Live-Charts by beto-rodriguez as my plot library, but i have some trouble with updating the graphs in real time. I know that i have to run multiple threads to update the UI in realtime, but every single way i tried doesn't work (i'm learning C# now).
I'm confused of how i should properly implement this pattern for the realtime update, and if the plot library is able to do that.
This is my actual code (its a simplified version of what i will do and doesn't implement any multithread code)
ModelView Code:
using System;
using System.ComponentModel;
using System.Windows;
using LiveCharts;
namespace TestMotionDetection
{
class MainViewModel : INotifyPropertyChanged
{
public SeriesCollection Series { get; set; }
public Func<double, string> YFormatter { get; set; }
public Func<double, string> XFormatter { get; set; }
public DataViewModel SData
{
set
{
Series[0].Values.Add(value);
OnPropertyChanged("Series");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MainViewModel()
{
SeriesConfiguration<DataViewModel> config = new SeriesConfiguration<DataViewModel>();
config.Y(model => model.Value);
config.X(model => model.Time);
Series = new SeriesCollection(config)
{
new LineSeries {Values = new ChartValues<DataViewModel>(), PointRadius = 0}
};
XFormatter = val => Math.Round(val) + " ms";
YFormatter = val => Math.Round(val) + " °";
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void generateData()
{
DataViewModel val = new DataViewModel();
for (int i = 0; i < 500; i++)
{
val.Time = i;
val.Value = i + 2.3f;
SData = val;
}
}
}
}
Here is the View code:
using System.Windows;
namespace TestMotionDetection
{
/// <summary>
/// Logica di interazione per MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MainViewModel vista;
public MainWindow()
{
vista = new MainViewModel();
DataContext = vista;
}
private void button_Click(object sender, RoutedEventArgs e)
{
vista.generateData();
}
}
}
And the XALM:
<Window x:Class="TestMotionDetection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lvc="clr-namespace:LiveCharts;assembly=LiveCharts"
Title="Example 2 (WPF)"
Width="1025.213"
Height="482.801">
<Grid>
<lvc:LineChart Margin="0,2,245,-2"
LegendLocation="Right"
Series="{Binding Series}">
<lvc:LineChart.AxisX>
<lvc:Axis LabelFormatter="{Binding XFormatter}" Separator="{x:Static lvc:DefaultAxes.CleanSeparator}" />
</lvc:LineChart.AxisX>
<lvc:LineChart.AxisY>
<lvc:Axis LabelFormatter="{Binding YFormatter}" />
</lvc:LineChart.AxisY>
</lvc:LineChart>
<Button x:Name="button"
Width="151"
Height="79"
Margin="804,47,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Click="button_Click"
Content="Button" />
</Grid>
</Window>
[UPDATE 1]
[]1
Your XFormatter & YFormatter should both be like this:
private Func<double, string> _yFormatter;
public Func<double, string> YFormatter {
get{ return _yFormatter; }
set
{
_yFormatter = value;
OnPropertyChanged("YFormatter");
}
If you are using C#6 you should do nameof(YFormatter) instead.
That will cause the view to update, otherwise the view has no way of knowing that the formatter changed.
Related
I'm trying to implement a language selection system for my modernui wpf application. When language is switched from the combobox this change would be propagated to all of the application controls by means of databinding.
I've built the skeleton as follows:
languages are objects collected in a List
strings and their counterparts are stored in a static dictionary
data for binding is fetched by a Translation object
views have binding to the datacontext which is based on language framework
Below is the stripped down version of the functionalty and link to the sample vs2013 project. I tried INotify.. without success, I could only manage to update the binding target by resetting datacontext of the view (page1.xaml). Unfortunately couldn't update target on the other view (home.xaml). Question comes down to: "How to update all targets in all views at the same time?"
I'll appreciate any help and suggestions to setup a proper binding structure.
download sample project: http://goo.gl/yjSsKm
home.xaml
<Grid Style="{StaticResource ContentRoot}">
<ScrollViewer>
<StackPanel MinWidth="200">
<TextBlock Text="{Binding home_text_1}"/>
</StackPanel>
</ScrollViewer>
</Grid>
page1.xaml
<Grid Style="{StaticResource ContentRoot}">
<ScrollViewer>
<StackPanel MinWidth="200">
<TextBlock Text="{Binding page1_text_1}"/>
<ComboBox x:Name="cbox_lang" Width="120" HorizontalAlignment="Left" Margin="0,50,0,0" SelectionChanged="cbox_lang_SelectionChanged"/>
</StackPanel>
</ScrollViewer>
</Grid>
MainWindow.xaml.cs
using DynamicDataBinding.Pages;
using FirstFloor.ModernUI.Windows.Controls;
namespace DynamicDataBinding
{
public partial class MainWindow : ModernWindow
{
public MainWindow()
{
InitializeComponent();
Language lang_1 = new Language("Language 1");
Language lang_2 = new Language("Language 2");
Global.availableLanguages.Add(lang_1);
Global.availableLanguages.Add(lang_2);
Global.currentLanguage = lang_1;
Global.currentLanguage.set();
}
}
}
page1.xaml.cs
using System.Collections.Generic;
using System.Windows.Controls;
namespace DynamicDataBinding.Pages
{
public partial class Page1 : UserControl
{
public Page1()
{
InitializeComponent();
foreach (Language lang in Global.availableLanguages)
{
cbox_lang.Items.Add(lang.Name);
}
cbox_lang.SelectedItem = Global.currentLanguage.Name;
this.DataContext = Global.currentLanguage.FrameWork;
}
private void cbox_lang_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cbox_lang.SelectedItem.ToString() != Global.currentLanguage.Name)
{
string selectedLanguage = cbox_lang.SelectedItem.ToString();
Global.currentLanguage = Global.availableLanguages.Find(lang => lang.Name == selectedLanguage);
Global.currentLanguage.set();
DataContext = null;
DataContext = Global.currentLanguage.FrameWork;
}
}
}
public class Global
{
public static Dictionary<string, string> dictionary = new Dictionary<string, string>();
public static List<Language> availableLanguages = new List<Language>();
public static Language currentLanguage;
}
public class Language
{
public string Name;
public Translation FrameWork;
public Language(string name)
{
this.Name = name;
}
public void set()
{
Global.dictionary.Clear();
if (Global.currentLanguage.Name == "Language 1")
{
Global.dictionary.Add("home_content_1", "Content For Home in Language 1");
Global.dictionary.Add("page1_content_1", "Content For Page1 in Language 1");
}
else if (Global.currentLanguage.Name == "Language 2")
{
Global.dictionary.Add("home_content_1", "Different Content For Home in Language 2");
Global.dictionary.Add("page1_content_1", "Different Content For Page1 in Language 2");
}
FrameWork = new Translation();
}
}
public class Translation
{
public string home_text_1 { get { return Global.dictionary["home_content_1"]; } }
public string page1_text_1 { get { return Global.dictionary["page1_content_1"]; } }
}
}
home.xaml.cs
using System.Windows.Controls;
namespace DynamicDataBinding.Pages
{
public partial class Home : UserControl
{
public Home()
{
InitializeComponent();
this.DataContext = Global.currentLanguage.FrameWork;
}
}
}
At the end of the day (eventually) I figured out that there is no need to deal with datacontext. Implementing INotifyPropertyChanged handles all of the target updates if it is setup properly. Furthermore use of a dictionary is not suitable to hold data if binding is in question. Below is the corrected version of the code:
page1.xaml.cs
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Controls;
namespace DynamicDataBinding.Pages
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : UserControl
{
public Page1()
{
InitializeComponent();
foreach (Language lang in Global.availableLanguages)
{
cbox_lang.Items.Add(lang.Name);
}
cbox_lang.SelectedItem = Global.currentLanguage.Name;
this.DataContext = Global.FrameWork;
}
private void cbox_lang_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cbox_lang.SelectedItem.ToString() != Global.currentLanguage.Name)
{
string selectedLanguage = cbox_lang.SelectedItem.ToString();
Global.currentLanguage = Global.availableLanguages.Find(lang => lang.Name == selectedLanguage);
Global.currentLanguage.set();
DataContext = null;
DataContext = Global.FrameWork;
}
}
}
public class Global
{
public static Dictionary<string, string> dictionary = new Dictionary<string, string>();
public static List<Language> availableLanguages = new List<Language>();
public static Language currentLanguage;
public static Translation FrameWork = new Translation();
}
public class Language
{
public string Name;
public Language(string name)
{
this.Name = name;
}
public void set()
{
Global.dictionary.Clear();
if (Global.currentLanguage.Name == "Language 1")
{
Global.FrameWork.home_text_1 = "Content For Home in Language 1";
Global.FrameWork.page1_text_1 = "Content For Home in Language 1";
}
else if (Global.currentLanguage.Name == "Language 2")
{
Global.FrameWork.home_text_1 = "Different Content For Home in Language 2";
Global.FrameWork.page1_text_1 = "Different Content For Home in Language 2";
}
}
}
public class Translation : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _home_text_1;
private string _page1_text_1;
public string home_text_1 { get {return _home_text_1;} set { _home_text_1 = value; OnPropertyChanged("home_text_1"); }}
public string page1_text_1 { get {return _page1_text_1;} set { _page1_text_1 = value; OnPropertyChanged("page1_text_1");}}
}
}
I've been teaching myself C# and WPF for a few months and I've been stuck on this latest problem for a few days. I receive a stream of data (bytes of numbers and flags) about numerous units every second. I want to display this data using a UserControl and update it with data bindings. Since the project will require 42 such units displayed in a window, I have placed the UserControls in an array. But, I can't get the data binding to work. I've stripped out as much code as I could but I know this is still a lot remaining. The problem is with the bindings. I just can't figure out the correct syntax.
Here is My UserControl
<UserControl x:Name="TestUserControl"
x:Class="Test.UserControl1">
<Label x:Name="userControlNameLabel" Content="UNIT x" />
<Label x:Name="powerLabel" Content=" Powered"/>
<Label Content="V in"/>
<Label x:Name="vinData" Content="{Binding Path=VinDisplay, ElementName=UserControl1}" >
</Grid>
</UserControl>
Here is my UserControl code behind:
namespace Test
{
public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public UserControl1()
{
InitializeComponent();
}
private byte vin;
public byte Vin
{
get { return (byte)GetValue(VinProperty); }
set
{ SetValue(VinProperty, (byte)value);
NotifyPropertyChanged("Vin");
}
}
private int power;
public int Power
{
get { return power; }
set
{
power = value;
if (power == 1)
{
powerLabel.Background = LEDONCOLOR;
powerLabel.Foreground = BLACKFONT;
}
else
{
powerLabel.Background = LEDOFFCOLOR;
powerLabel.Foreground = WHITEFONT;
}
}
}
public string UserControlName
{
get { return userControlNameLabel.Content.ToString(); }
set { userControlNameLabel.Content = value; }
}
public static DependencyProperty VinProperty = DependencyProperty.Register("Vin", typeof(byte), typeof(UserControl));
public static DependencyProperty PowerProperty = DependencyProperty.Register("Power", typeof(int), typeof(UserControl));
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Here is the Observable Collection
namespace Test
{
class userData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public String name { get; private set; }
private byte voltageIn;
public byte VoltageIn
{
get
{
return voltageIn;
}
set
{
if (value != this.voltageIn)
{
this.voltageIn = value;
NotifyPropertyChanged();
}
}
}
private int power;
public int Power
{
get
{
return power;
}
set
{
if (value != this.power)
{
this.power = value;
NotifyPropertyChanged();
}
}
}
public userData(string name, byte voltageIn, int power)
{
this.name = name;
this.voltageIn = voltageIn;
this.power = power;
}
public userData(string name)
{
this.name = name;
this.voltageIn = 0x00;
this.power = 0;
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Here is my Main window
<Window x:Class="Test.MainWindow"
xmlns:src="clr-namespace:Test"
xmlns:local="clr-namespace:Test"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" >
<Grid>
<Grid>
<Label Content="HW 0"/>
<src:UserControl1 x:Name="hw0unit1" Vin="{Binding Path=_userData[0].VoltageIn, Mode=OneWay}" />
<src:UserControl1 x:Name="hw0unit2"/>
</Grid>
<Grid>
<Label Content="HW 1"/>
<src:UserControl1 x:Name="hw1unit1"/>
<src:UserControl1 x:Name="hw1unit2"/>
</Grid>
</Grid>
</Window>
And my main program
namespace Test
{
public partial class MainWindow : Window
{
DispatcherTimer hwScan;
UserControl1[] userCntrl = new UserControl1[4];
byte[] vinDisplay = new byte[4];
int[] powerDisplay = new int[4];
ObservableCollection<userData> _userData;
// Main Routine
public MainWindow()
{
InitializeComponent();
// Create dispatch timer to call hwTimer_Tick
...
// Save off UserControls for all units into Array for easier access
userCntrl[0] = hw0unit1;
userCntrl[1] = hw0unit2;
userCntrl[2] = hw1unit1;
userCntrl[3] = hw1unit2;
// Init userData arrays
_userData = new ObservableCollection<userData>();
for (int i = 0; i < Properties.Settings.Default.unit.Count; i++)
_userData.Add(new userData(Properties.Settings.Default.unit[i]));
}
private void hwTimer_Tick(object sender, EventArgs e)
{
// Hardcode data for testing
vinDisplay[i] = a hex constant
powerDisplay[i] = a hex constant
for (int i=0; i<4; i++)
{
_userData[i].VoltageIn = vinDisplay[i];
_userData[i].Power = powerDisplay[i];
}
}
}
}
I'm Trying to make a simple quiz program in Windows 8 using the MVVM design pattern. I tried to Use PRISM and MVVMlite but I'm a newb and simply don't have enough knowledge of data and control binding to understand how to use it correctly. I think I have the majority of it working but I a few major problems.
1. my GUI doesn't update properly.
2. I'm receiving several errors.
3. Fixing one part of my code breaks another part.
4. Can't figure out how to get "sender" information from command in XAML.
here is my code so far:
xml data:
<root>
<Object>
<Question>What do you do for work</Question>
<Answer>Wrestle giant tentical monsters</Answer>
<Choices>Battle robots</Choices>
<Choices>Glorious ruler of North Korea</Choices>
<Choices>Wrestle Giant Tentical Monsters</Choices>
<Choices>Defender of all that is good</Choices>
</Object>
<Object>
<Question>What do you drive</Question>
<Answer>Moped</Answer>
<Choices>Helicopter</Choices>
<Choices>Pegasus</Choices>
<Choices>Rocketship</Choices>
<Choices>Moped</Choices>
</Object>
</root>
Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Xml.Linq;
using System.Windows.Input;
namespace Quiz
{
class QuizModel : INotifyPropertyChanged
{
private string _question;
public string Question
{
get { return _question; }
set
{
_question = value;
OnPropertyChanged("Question");
}
}
private string _answer;
public string Answer
{
get { return _answer; }
set
{
_answer = value;
OnPropertyChanged("Answer");
}
}
private List<string> _choices;
public List<string> Choices
{
get { return _choices; }
set
{
_choices = value;
OnPropertyChanged("Choices");
}
}
public QuizModel(string quesiton, string answer, List<string> choices)
{
Question = quesiton;
Answer = answer;
Choices = choices;
}
public static List<QuizModel> Query(string datasource)
{
XElement quizdata = XElement.Load(datasource);
List<QuizModel> query = (from d in quizdata.Descendants("Object")
select new QuizModel(
(string)d.Element("Question"),
(string)d.Element("Answer"),
d.Elements("Choices").Select(a => a.Value).ToList()
)).ToList();
return query;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
View Model:
class QuizViewModel
{
public static List<QuizModel> QuizList { get; set; }
public static QuizModel Quiz { get; set; }
public static int Indexer { get; set; }
public ICommand myCommand { get; set; }
//Initiallizes view model
public QuizViewModel()
{
Indexer = 0;
QuizList = QuizModel.Query("Quiz.xml");
Quiz = QuizList[Indexer];
myCommand = new ActionCommand(Evaluate);
}
//Increments to next question
private void Evaluate()
{
Indexer++;
Quiz = QuizList[Indexer];
}
}
iCommand:
public class ActionCommand : ICommand
{
private readonly Action _action;
public ActionCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged; //ERROR event Never Used
}
}
View:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<TextBlock FontSize="50" Text="{Binding Quiz.Question}">
<TextBlock.DataContext>
<local:QuizViewModel/> <!--Can't find Quiz.xml-->
</TextBlock.DataContext>
</TextBlock>
<ListView Grid.Row="1" FontSize="30" ItemsSource="{Binding Quiz.Choices}">
<ListView.DataContext>
<local:QuizViewModel/> <!--Can't find Quiz.xml-->
</ListView.DataContext>
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Mode=OneWay}" Command="{Binding myCommand}">
<Button.DataContext>
<local:QuizViewModel/>
</Button.DataContext>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
I have 3 current errors 2 of which are the same
first error refers to in the XAMLs datacontext:
Error 1 (x2)
Could not find file 'C:\Users\Me\AppData\Local\Microsoft\VisualStudio\11.0\Designer\ShadowCache\cv0te54x.fpv\5ncl4yxi.hui\Quiz.xml'.
Error 2
Cannot create instance of type 'Quiz.QuizViewModel'
This seems to effect my "Choices" not populating, I can fix this by removing the data context, but then I can't bind "myCommand"
Third problem is how do I the Sender information from the command input so I can evaluate if it is right or wrong?
Take a look at the error #1, your code can't find the Quiz.xml file and it is looking for it at the location in the error description. It seems it's looking at the wrong location, so you might have to specify a more concrete path for it. This question may help, if you have the xml file in the resources. Because this is done in the Quiz.QuizViewModel constructor, the creating of the instance fails and produces error #2.
As for the third part, you can pass the command an arbitrary parameter. In this case it might be the choice or its position. Something like this
Is there a MVVM way to select text in a textbox? The MVVM framework that I am using is Laurent Bugnion's MVVM Light Toolkit.
Whenever I am trying to directly affect the the View in a "pure" MVVM application (no code-behind in View), I will use Attached Properties to encapsulate whatever effect I am trying to achieve. I will create an interface that defines the actions I wish to take using custom events. I then implement this interface in each ViewModel that will be "running" these commands on the View. Finally, I bind my ViewModel to the attached property in my View definition. The following code shows how to this for SelectAll and a TextBox. This code can be easily expanded to perform just about any action on any component in the View.
My Attached Property and interface definition:
using System.Windows;
using System.Windows.Controls;
using System;
using System.Collections.Generic;
namespace SelectAllSample
{
public static class TextBoxAttach
{
public static readonly DependencyProperty TextBoxControllerProperty = DependencyProperty.RegisterAttached(
"TextBoxController", typeof(ITextBoxController), typeof(TextBoxAttach),
new FrameworkPropertyMetadata(null, OnTextBoxControllerChanged));
public static void SetTextBoxController(UIElement element, ITextBoxController value)
{
element.SetValue(TextBoxControllerProperty, value);
}
public static ITextBoxController GetTextBoxController(UIElement element)
{
return (ITextBoxController)element.GetValue(TextBoxControllerProperty);
}
private static readonly Dictionary<ITextBoxController, TextBox> elements = new Dictionary<ITextBoxController, TextBox>();
private static void OnTextBoxControllerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = d as TextBox;
if (element == null)
throw new ArgumentNullException("d");
var oldController = e.OldValue as ITextBoxController;
if (oldController != null)
{
elements.Remove(oldController);
oldController.SelectAll -= SelectAll;
}
var newController = e.NewValue as ITextBoxController;
if (newController != null)
{
elements.Add(newController, element);
newController.SelectAll += SelectAll;
}
}
private static void SelectAll(ITextBoxController sender)
{
TextBox element;
if (!elements.TryGetValue(sender, out element))
throw new ArgumentException("sender");
element.Focus();
element.SelectAll();
}
}
public interface ITextBoxController
{
event SelectAllEventHandler SelectAll;
}
public delegate void SelectAllEventHandler(ITextBoxController sender);
}
My ViewModel definition:
public class MyViewModel : ITextBoxController
{
public MyViewModel()
{
Value = "My Text";
SelectAllCommand = new RelayCommand(p =>
{
if (SelectAll != null)
SelectAll(this);
});
}
public string Value { get; set; }
public RelayCommand SelectAllCommand { get; private set; }
public event SelectAllEventHandler SelectAll;
}
My View definition:
<Window x:Class="SelectAllSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:SelectAllSample"
Title="Window1" Height="150" Width="150">
<x:Code><![CDATA[
public Window1()
{
InitializeComponent();
DataContext = new MyViewModel();
}
]]></x:Code>
<StackPanel>
<TextBox Text="{Binding Value}" loc:TextBoxAttach.TextBoxController="{Binding}" />
<Button Content="Select All" Command="{Binding SelectAllCommand}" />
</StackPanel>
</Window>
Note: Thanks to Josh Smith for RelayCommand (see code in Figure 3 on this page). It is used in MyViewModel in this example (and just about all my MVVM code).
find a good introduction to attached properties here:
http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx
I'm gonna write a code with DataBinding and a Timer to change an Image sequentially.
e.g: in each two seconds.
below is my C# code :
public class GenerateRandomImagePath
{
Random random = new Random((int)DateTime.Now.Ticks);
readonly int MinInt;
readonly int MaxInt;
readonly string PrefixImagesName;
readonly string ImageExtension;
/// <summary>
/// Used in data binding
/// </summary>
public string ImageFullPath { get; set; }
public GenerateRandomImagePath(string prefixName, string extension)
{
this.PrefixImagesName = prefixName;
this.MinInt = 1;
this.MaxInt = 100;
this.ImageExtension = extension;
}
int RandomNumber()
{
return random.Next(this.MinInt, this.MaxInt);
}
public void GenerateNewRandomImagePath()
{
this.ImageFullPath = this.PrefixImagesName + RandomNumber() + this.ImageExtension;
}
}
public partial class MainWindow : Window
{
GenerateRandomImagePath RandomImagePath;
System.Timers.Timer timer = new System.Timers.Timer(2000);
public MainWindow()
{
InitializeComponent();
RandomImagePath = new GenerateRandomImagePath(#"C:\Users\MDS\Pictures\Nature 02\Nature ", #".jpg");
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Enabled = true;
this.DataContext = RandomImagePath;
RandomImagePath.GenerateNewRandomImagePath();//this line works well
}
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
RandomImagePath.GenerateNewRandomImagePath();
}
}
The XAML code :
<Window
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"
x:Class="sth.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="800" Height="600" mc:Ignorable="d">
<Grid x:Name="LayoutRoot">
<Image Source="{Binding Path=ImageFullPath}" />
</Grid>
</Window>
The code works just for first time! After that, the timer changes ImageSource but it doesn't effect on view!
Would you please guide me?
Thanks
According to your code, the GenerateRandomImagePath class doesn't implement INotifyPropertyChanged. WPF can't know that the ImageFullPath has changed unless you tell it, either by implementing that interface or by changing the class to derive from DependencyObject and turning the property into a dependency property.
I would suggest implementing INotifyPropertyChanged - it's a more lightweight approach.
public class GenerateRandomImagePath : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
private string _imageFullPath;
public string ImageFullPath
{
get { return _imageFullPath; }
set
{
_imageFullPath = value;
OnPropertyChanged("ImageFullPath");
}
}
}