I'm trying to create a WP8 app which gets data from a website and displays them.
I chose the panorama template and Visual Studio created some default code.
What I am trying to do is, that the textblock automatically gets updated if I change the variable the text is bound to. But calling changeDate() doesn't change the UI. The textbox still says "dd.mm.yyyy".
MainPage.xaml:
<phone:LongListSelector.ListHeaderTemplate>
<DataTemplate>
<Grid Margin="12,0,0,38">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock
Text="{Binding Date}"
Style="{StaticResource PanoramaItemHeaderTextStyle}"
Grid.Row="0">
<TextBlock.DataContext>
<ViewModels:MainViewModel/>
</TextBlock.DataContext>
</TextBlock>
</Grid>
</DataTemplate>
</phone:LongListSelector.ListHeaderTemplate>
.
MainViewModel.cs:
public class MainViewModel : INotifyPropertyChanged
{
[...]
private string _date = "dd.mm.yyyy";
public string Date
{
get
{
return _date;
}
set
{
if (value != _date)
{
_date = value;
NotifyPropertyChanged("Date");
}
}
}
//public void changeDate()
//{
// Date = "fu";
// App.ViewModel.Date = "bar";
//}
**UPDATE 2**
public bool IsDataLoaded
{
get;
private set;
}
public void LoadData()
{
System.Net.WebClient wc = new System.Net.WebClient();
wc.DownloadStringCompleted += wc_DownloadStringCompleted;
wc.DownloadStringAsync(new Uri("somelink"));
}
private void wc_DownloadStringCompleted(object sender, System.Net.DownloadStringCompletedEventArgs e)
{
string s = FilterData(e.Result);
}
private string FilterData(string s)
{
string[] split = System.Text.RegularExpressions.Regex.Split(s, "<tbody>");
s = split[1];
split = System.Text.RegularExpressions.Regex.Split(s, "</tbody>");
s = split[0];
split = System.Text.RegularExpressions.Regex.Split(s, "\r\n");
foreach(string str in split)
{
if (str.Contains("class=\"xl24\""))
{
App.ViewModel.Date = "somedate";
}
}
return s;
}
**END UPDATE 2**
[...]
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
UPDATE 1
MainPage.xaml.cs:
public MainPage()
{
InitializeComponent();
DataContext = App.ViewModel;
}
**UPDATE 2**
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
**END UPDATE 2**
[...]
.
App.xaml.cs:
private static MainViewModel viewModel = null;
public static MainViewModel ViewModel
{
get
{
if (viewModel == null)
viewModel = new MainViewModel();
return viewModel;
}
}
[...]
What I think is happening is that your NotifyPropertyChanged is called from some worker thread which could result in calling Date getter from the same worker thread. If an UI element calls data getter from worker thread (instead from Main UI thread) operation ends with "Invalid cross-thread access". If that's so, you should make handler call from Main thread. For example:
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
Dispatcher dsp = Deployment.Current.Dispatcher;
dsp.BeginInvoke(() => {
handler(this, new PropertyChangedEventArgs(propertyName));
});
}
}
Hope it helps
<TextBlock.DataContext>
<ViewModels:MainViewModel/>
</TextBlock.DataContext>
This creates a new MainViewModel object and you are not updating this object but the one you stored in the App object.
To solve this: set the data context of the View to the App.ViewModel object (no need to set the data context of the TextBlock)
Extra: Please do NOT use this code:
public void changeDate()
{
Date = "fu";
App.ViewModel.Date = "bar";
}
Now your ViewModel knows about the App. Just use:
public void changeDate()
{
Date = "fu";
}
Related
I have a AdvancedCollectionView from Windows Community Toolkit version 6.1.1 and trying to use it to filter out on 2 string properties.
I have created a simple app to reproduce the issue : https://github.com/touseefbsb/UWP-Filter-List
It has a textbox for filtering between StartNumber and EndNumber properties of items.
but as soon as I enter text "123" into it, it shows no item in the ListView when it should actually show only the first item, based on the test logic.
Code
MainPage.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox
x:Name="SearchTicketBooksBox"
Width="300"
Header="Search"
TextChanged="SearchTicketBooks_TextChanged" />
<ListView
x:Name="TicketBooksListView"
Grid.Row="1"
Margin="4,0,0,0"
ItemsSource="{x:Bind ViewModel.TicketBooks}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="app1:TicketBookDto">
<StackPanel Margin="20">
<TextBlock Text="{x:Bind StartNumber}" />
<TextBlock Text="{x:Bind EndNumber}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainViewModel ViewModel { get; }
public MainPage() { InitializeComponent(); ViewModel = new MainViewModel(); }
private void SearchTicketBooks_TextChanged(object sender, TextChangedEventArgs e)
{
if (ViewModel != null)
{
if (string.IsNullOrWhiteSpace(SearchTicketBooksBox.Text))
{
ViewModel.TicketBooks.Filter = _ => true;
}
else
{
ViewModel.TicketBooks.Filter = x => ((TicketBookDto)x).StartNumber == SearchTicketBooksBox.Text;
//{
// var startNumber = Convert.ToInt32(((TicketBookDto)x).StartNumber);
// var endNumber = Convert.ToInt32(((TicketBookDto)x).EndNumber);
// var searchText = Convert.ToInt32(SearchTicketBooksBox.Text);
// return searchText >= startNumber && searchText <= endNumber;
//};
}
}
}
private void Page_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
ViewModel.TicketBooks.Add(new TicketBookDto { StartNumber = "123", EndNumber = "456" });
ViewModel.TicketBooks.Add(new TicketBookDto { StartNumber = "789", EndNumber = "987" });
}
}
MainViewModel
public class MainViewModel : INotifyPropertyChanged
{
private readonly ObservableCollection<TicketBookDto> _ticketBooksPrivate = new ObservableCollection<TicketBookDto>();
private AdvancedCollectionView ticketBooks;
public AdvancedCollectionView TicketBooks
{
get
{
if (ticketBooks is null)
{
ticketBooks = new AdvancedCollectionView(_ticketBooksPrivate, true);
ticketBooks.ObserveFilterProperty(nameof(TicketBookDto.StartNumber));
ticketBooks.ObserveFilterProperty(nameof(TicketBookDto.EndNumber));
}
return ticketBooks;
}
set => Set(ref ticketBooks, value);
}
#region INotifyStuff
public event PropertyChangedEventHandler PropertyChanged;
protected void Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
}
TicketBookDto
public partial class TicketBookDto
{
public string StartNumber { get; set; }
public string EndNumber { get; set; }
}
I have commented out code of the actual logic I need in the filter, which is to show the list items only those who have the search number between their "StartNumber" and "EndNumber" properties. But for simplicity I am using a simple match to StartNumber logic to find out why it aint working. Also when I add a boolean property and simply filter using that property then it works fine, its just not working with these string properties.
I'm afraid you can't use Filter in TextChanged event, please refer the source code here.
if (_filter != null)
{
for (var index = 0; index < _view.Count; index++)
{
var item = _view.ElementAt(index);
if (_filter(item))
{
continue;
}
RemoveFromView(index, item);
index--;
}
}
When you input text in the SerchBox and if the text is not equal to the filter property, the item will be RemoveFromView. So it will make the next input not effect. For this design, we suggest you make submit button to filter the data after finished input.
And If you do want to filter in the TextChanged, you could call ViewModel.TicketBooks.Filter = _ => true; to reset the viewtiems before set Filter delegate method like the following.
private void SearchTicketBooks_TextChanged(object sender, TextChangedEventArgs e)
{
if (ViewModel != null)
{
if (string.IsNullOrWhiteSpace(SearchTicketBooksBox.Text))
{
ViewModel.TicketBooks.Filter = _ => true;
}
else
{ // reset the filter
ViewModel.TicketBooks.Filter = _ => true;
ViewModel.TicketBooks.Filter = x => ((TicketBookDto)x).EndNumber == SearchTicketBooksBox.Text;
}
}
}
I am developing a GUI where a user can connect to server and read the data. The data needs to be displayed on the GUI. For this I am using TabControl whose ContentTemplate is set to RichTextBox. The XAML code is as below
<TabControl x:Name="tabControl1" HorizontalAlignment="Stretch" MinHeight="50" Margin="0,0,0,0.2" Width="884"
ItemsSource="{Binding Titles, Mode=TwoWay}" Height="454" VerticalAlignment="Bottom">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<RichTextBox Margin="10" VerticalScrollBarVisibility="Visible" >
<FlowDocument>
<Paragraph FontSize="12" FontFamily="Courier New">
<Run Text="{Binding Content}"></Run>
</Paragraph>
</FlowDocument>
</RichTextBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
The background code for adding new tab and setting its header/content (static) is below
public class MainWindowVM : INotifyPropertyChanged
{
public MainWindowVM()
{
Titles = new ObservableCollection<Item>();
}
public class Item
{
public string Header { get; set; }
public string Content { get; set; }
}
public ObservableCollection<Item> Titles
{
get { return _titles; }
set
{
_titles = value;
OnPropertyChanged("Titles");
}
}
static int tabs = 1;
private ObservableCollection<Item> _titles;
private ICommand _addTab;
private ICommand _removeTab;
public ICommand AddTab
{
get
{
_addTab = new TabRelayCommand(
x =>
{
AddTabItem();
});
return _addTab;
}
}
public ICommand RemoveTab
{
get
{
_removeTab = new TabRelayCommand(
x =>
{
RemoveTabItem();
});
return _removeTab;
}
}
private void RemoveTabItem()
{
if (Titles.Count > 0)
{
Titles.Remove(Titles.Last());
tabs--;
}
}
public Item AddTabItem()
{
var header = "Log_" + tabs;
var content = "Content " + tabs;
var item = new Item { Header = header, Content = content };
Titles.Add(item);
tabs++;
OnPropertyChanged("Titles");
return item;
}
public void AddTabItem(string strFileName, string strContent)
{
var header = strFileName;
var content = strContent;
var item = new Item { Header = header, Content = content };
Titles.Add(item);
tabs++;
OnPropertyChanged("Titles");
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
However I need to set the content of the header dynamically (the data read from the socket). I can read the data I have it in string format.
Kindly suggest me how do I set the content of RichTextBox by appending the string.
I am new to C#. Thanks in advance
Edit:
After button click event, my application gets connected to the server. Also I start a parallel task which reads data from the socket.
Problem facing: Too much of CPU time taken (I can see upto 80 under Processes in Task Manager).
private void BtnConnect_Click(object sender, RoutedEventArgs e)
{
//connect
TCPClientClass tcpClient = TCPConnHandler.ConnectToService(tbIPAddress.Text);
if (tcpClient != null)
{
MessageBox.Show("Connected to " + tbIPAddress.Text);
//open new tab
var item = MainWindowVMObj.AddTabItem();
//now run a task to display the data in the tab
Thread thTabControl = new Thread(() =>
{
while (tcpClient.Connected)
{
String str = tcpClient.GetDataFromServer();
if (!String.IsNullOrEmpty(str))
tabControl1.Dispatcher.BeginInvoke((Action)(() => item.Content += str));
Thread.Sleep(200);
}
//item.Dispatcher.BeginInvoke
});
thTabControl.Start();
}
}
You could look up the Item to update in Titles and set its Content property.
For example, this sets the property of the first item (index 0) in the ObservableCollection<Item>:
Titles[0].Content += "append...";
The Item class should also implement INotifyPropertyChanged for you to see the changes without switching tabs:
public class Item : INotifyPropertyChanged
{
public string Header { get; set; }
private string _content;
public string Content
{
get { return _content; }
set { _content = value; OnPropertyChanged(nameof(Content)); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I don't if I am doing something wrong or if there is something I am missing but the INotifyPropertyChanged works when I do it with compile time binding and doesn't work when I do it with traditional binding.
public class Students : INotifyPropertyChanged
{
private string name;
private string surname;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string Name
{
get { return name; }
set
{
if(value != name)
{
name = value;
NotifyPropertyChanged();
}
}
}
public string Surname
{
get { return surname; }
set
{
if (value != surname)
{
surname = value;
NotifyPropertyChanged();
}
}
}
public Students()
{
Name = "John";
Surname = "Smith";
}
}
MainPage.xaml
<Page.DataContext>
<local:Students/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Margin="0,200,0,0">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Surname}"/>
<Button Content="Change Name" Click="change_name"/>
<Button Content="Change Surname" Click="change_surname"/>
</StackPanel>
</Grid>
MianPage.xaml.cs
public sealed partial class MainPage : Page
{
Students st;
public MainPage()
{
this.InitializeComponent();
st = new Students();
}
private void change_name(object sender, RoutedEventArgs e)
{
st.Name = "MM";
}
private void change_surname(object sender, RoutedEventArgs e)
{
st.Surname = "SS";
}
}
I am really confused, because when you bind with compile time binding, it works fine. What's going on?
I don't see any place in which you are setting the current DataContext to your object.
public MainPage()
{
this.InitializeComponent();
st = new Students();
this.DataContext = st;
}
OR: You are setting a datacontext in your XAML, but you aren't referencing it.
<Page.DataContext>
<local:Students/>
</Page.DataContext>
You would need to reference that object from code if you intend to use it.
private void change_name(object sender, RoutedEventArgs e)
{
((Students)this.DataContext).Name = "MM";
}
I had a situation very similar to the author (if not the same) and only thing I did to fix it is I made my view model a public property. In case of the example above, it would be Students st; changed to public Students st {get; set;}
I have a UserControl that I add to my main application.
That UserControl contains a button for a UIElement
The UserControl contains a DispatchTimer and every 2 seconds based on some int values determines what the button image will be.
One of the methods called in the UserControl should set it's image but the control never displays the image that it was changed to.
public void SetNormal()
{
btnFlashAlert.Content = new BitmapImage(new Uri("Images/FlashButton.png", UriKind.RelativeOrAbsolute));
}
Is there something i'm missing to get the look of the control update on the main application?
When I look at what .Content contains, it is correct. The UI doesn't reflect the change.
XAML
<UserControl x:Class="SC.FlashSystem.MainButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="53" Width="164">
<Button x:Name="btnFlashAlert" Background="{x:Null}" BorderBrush="{x:Null}" Cursor="Hand" Click="btnFlashAlert_Click">
<Button.Template>
<ControlTemplate>
<Image Source="Images/FlashButton.png"/>
</ControlTemplate>
</Button.Template>
</Button>
Codebehind Updated
public partial class MainButton : UserControl
{
private SupportConsoleWeb.MessageData messageCounts { get; set; }
private readonly DispatcherTimer flashButtonChangeTimer = new DispatcherTimer();
private BitmapImage NormalImage { get; set; }
private BitmapImage CriticalImage { get; set; }
private BitmapImage AlertImage { get; set; }
private BitmapImage InfoImage { get; set; }
public MainButton()
{
InitializeComponent();
messageCounts = new SupportConsoleWeb.MessageData();
messageCounts.CriticalCount = 0;
messageCounts.AlertCount = 0;
messageCounts.InfoCount = 0;
NormalImage = new BitmapImage(new Uri("Images/FlashButton.png", UriKind.RelativeOrAbsolute));
CriticalImage = new BitmapImage(new Uri("Images/FlashButtonRed.png", UriKind.RelativeOrAbsolute));
AlertImage = new BitmapImage(new Uri("Images/FlashButtonOrange.png", UriKind.RelativeOrAbsolute));
InfoImage = new BitmapImage(new Uri("Images/FlashButtonGreen.png", UriKind.RelativeOrAbsolute));
flashButtonChangeTimer.Interval = TimeSpan.FromSeconds(2);
flashButtonChangeTimer.Tick += flashButtonChangeTimer_Tick;
flashButtonChangeTimer.Start();
}
void flashButtonChangeTimer_Tick(object sender, EventArgs e)
{
btnFlashAlert.Dispatcher.BeginInvoke(new Action(() =>
{
if (btnFlashAlert.Content == null)
{
SetNormal();
}
else if (messageCounts.CriticalCount > 0 && btnFlashAlert.Content.Equals(CriticalImage))
{
SetNormal();
}
else if (messageCounts.AlertCount > 0 && btnFlashAlert.Content.Equals(AlertImage))
{
SetNormal();
}
else if (messageCounts.InfoCount > 0 && btnFlashAlert.Content.Equals(InfoImage))
{
SetNormal();
}
else if (messageCounts.CriticalCount > 0)
{
SetCritical();
}
else if (messageCounts.AlertCount > 0)
{
SetAlert();
}
else if (messageCounts.InfoCount > 0)
{
SetInfo();
}
}));
}
public void UpdateMessageCounts(SupportConsoleWeb.MessageData messageCounts)
{
this.messageCounts = messageCounts;
}
private void btnFlashAlert_Click(object sender, RoutedEventArgs e)
{
MainWindow window = new MainWindow();
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
window.ShowDialog();
}
public void SetMessageCount(int criticalCount, int alertCount, int infoCount)
{
messageCounts.CriticalCount = criticalCount;
messageCounts.AlertCount = alertCount;
messageCounts.InfoCount = infoCount;
}
private void SetNormal()
{
btnFlashAlert.Content = NormalImage;
}
private void SetCritical()
{
btnFlashAlert.Content = CriticalImage;
}
private void SetAlert()
{
btnFlashAlert.Content = AlertImage;
}
private void SetInfo()
{
btnFlashAlert.Content = InfoImage;
}
}
Change your XAML To this
<Image Source="{Binding TheImage}"/>
Add notify property changed
public partial class MainButton : UserControl, INotifyPropertyChanged
Create the OnPropertyChanged Event
void OnPropertyChanged(String prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
public event PropertyChangedEventHandler PropertyChanged;
Create a Bitmap prop and notify the prop changed event
private BitmapImage _TheImage;
public BitmapImage TheImage
{
get { return _TheImage; }
set { _TheImage = value; OnPropertyChanged("TheImage"); }
}
In your initializer
public MainButton()
{
this.DataContext = this;
InitializeComponent();
TheImage = new BitmapImage();
Now in your setting methods call
TheImage = //Your Bitmap Goes here
I know this seems excessive but you will see it is a much cleaner implementation in the long run.
I believe its an issue with picture selection logic not having a default image when none of the conditions are met...
With that said, IMHO the picture logic would be better expressed by having all images pre-loaded and their visibility initially set to hidden. Then bind the visibility of each image to a specific flag boolean on the VM. Which the timer event can simply turn on or off the boolean(s) which will ultimately show or hide images as needed.
That removes any latency due to loading and showing of images for they will be pre-loaded; also it will solve any possible future memory issues due to loading/unloading of images.
Example
The following example has a button with two images. Both image's visibility is bound to Booleans on the VM. The VM has one Boolean which the imageas work off of and a timer which changes its status every two seconds switching the images.
Xaml:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Button x:Name="bStatus" Width="48" Height="48">
<StackPanel Orientation="Vertical">
<Image Source="Images\Copy-icon.png" Visibility="{Binding IsCopyOn,
Converter={StaticResource BooleanToVisibilityConverter}}" />
<Image Source="Images\Recycle-icon.png"
Visibility="{Binding IsRecycleOn,
Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</Button>
VM
public class MainVM : INotifyPropertyChanged
{
private bool _bSwitch;
private readonly DispatcherTimer flashButtonChangeTimer = new DispatcherTimer();
public bool IsRecycleOn
{
get { return _bSwitch; }
}
public bool IsCopyOn
{
get { return !_bSwitch; }
}
public MainVM()
{
flashButtonChangeTimer.Interval = TimeSpan.FromSeconds(2);
flashButtonChangeTimer.Tick += (sender, args) =>
{
_bSwitch = ! _bSwitch;
OnPropertyChanged("IsCopyOn");
OnPropertyChanged("IsRecycleOn");
};
flashButtonChangeTimer.Start();
}
/// <summary>Event raised when a property changes.</summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>Raises the PropertyChanged event.</summary>
/// <param name="propertyName">The name of the property that has changed.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I am using INotifyPropertyChanged but it will give me null when I shaw the PropertyChanged so what i can do..
my code is like this..
public class Entities : INotifyPropertyChanged
{
public Entities(int iCount)
{
_iCounter = iCount;
}
private int _iCounter;
public int iCounter
{
get
{
return _iCounter;
}
set
{
value = _iCounter;
NotifyPropertyChanged("iCounter");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Thanks...
I tried putting your code in my program and it is working fine. I am getting the EventArg as the property:
class Program
{
static void Main(string[] args)
{
var ent = new Entities(10);
ent.PropertyChanged += new PropertyChangedEventHandler(ent_PropertyChanged);
ent.iCounter = 100;
}
static void ent_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
}
public class Entities : INotifyPropertyChanged
{
public Entities(int iCount)
{
_iCounter = iCount;
}
private int _iCounter;
public int iCounter
{
get
{
return _iCounter;
}
set
{
_iCounter = value;
NotifyPropertyChanged("iCounter");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
What is the exact erro you are getting?
This is i think a bug in INotifyPropertyChanged .
There can be 2 workaround
1st Workaround
1- Assign iCounter property to a UI control like Lable.
2- Now change the value of the property this time , PropertyChanged event will have a reference of your method and will not be null;
2nd workaround
Assign PropertyChanged delegate in the Entities class constructor
i am giving the demo code in WPF
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.Resources>
<ToolTip x:Key="#tooltip">
<TextBlock Text="{Binding CompanyName}"/>
</ToolTip>
</Grid.Resources>
<TextBlock Text="{Binding Name}" Background="LightCoral" />
<Rectangle Width="200" Height="200" Fill="LightBlue" VerticalAlignment="Center" HorizontalAlignment="Center" ToolTip="{DynamicResource #tooltip}" Grid.Row="1"/>
<Button Click="Button_Click" Grid.Row="2" Margin="20">Click Me</Button>
</Grid>
see here CompanyName is assigned to a tool tip.
// this is Window1.Cs file
public Window1()
{
DataContext = DemoCustomer.CreateNewCustomer();
InitializeComponent();
}
// Now DemoCustomer Class
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerName = String.Empty;
private string companyNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerName = "no data";
companyNameValue = "no data";
phoneNumberValue = "no data";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CompanyName
{
get { return this.companyNameValue; }
set
{
if (value != this.companyNameValue)
{
this.companyNameValue = value;
OnPropertyChanged("CompanyName");
}
}
}
public string PhoneNumber
{
get { return this.phoneNumberValue; }
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
OnPropertyChanged("PhoneNumber");
}
}
}
}
and finally changing the value
private void Button_Click(object sender, RoutedEventArgs e)
{
DemoCustomer dc = this.DataContext as DemoCustomer;
dc.CompanyName = "Temp";
}