Show text file data in textbox with Data binding - c#

I am trying to read the text file in textbox with the help of data binding.
Here is the code of class where i used get and set properties
public class FileData : INotifyPropertyChanged
{
public string data;
public string Data
{
get { return data; }
set
{
data = value;
OnPropertyChanged();
}
}
public FileData(string data)
{
Data = data;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
}
}
And this is the code of Mainpage.Xaml.cs
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
display.DataContext = fd;
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(filenamebox.Text + ".txt");
fd.Data = await FileIO.ReadTextAsync(file);
}
And when i read the file this exception comes out
"Object reference not set to an instance of an object."
please help me :(

Check if file return is null or not
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync(filenamebox.Text + ".txt");
if ( file != null)
{
// Do what you want
}

Related

Property not updating after service completes

I'm trying to get my head around data binding in Xamarin.Forms. I've read lots of the guides and played with some examples and I am now trying to implement some of my own basic binding.
I've got a Strings file in which I've declared an empty variable:
public static class Strings
{
public static string UserDisplayName;
}
On load of my View, it runs an async function to grab data from a Azure SQL DB which then populates the string
Strings.UserDisplayName = user.FirstName;
In my view page I've bound a label to a variable userDisplayNm
<Label Text="{Binding UserDisplayNm}"></Label>
In my ViewModel I have the following to set UserDisplayNm, however it only ever returns "Welcome, ". How do i get it to fire this again after the sync service has completed & the Strings.UserDisplayname value changes? I think I'm missing a link to an PropertyChanged event or something?
namespace Travel_Comp.ViewModels
{
public sealed class MenuViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MenuViewModel()
{
this.UserDisplayNm = Strings.UserDisplayName;
}
public string UserDisplayNm
{
set
{
if (Strings.UserDisplayName != value)
{
value = Strings.UserDisplayName;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("UserDisplayNm"));
}
}
}
get
{
return "Welcome, " + Strings.UserDisplayName;
}
}
}
}
EDIT:
Thanks for your replies. I think I'm getting closer based on the replies below, here is what I've now got, although The MenuViewModel.LoadAsync() is throwing an error "Inaccessible due to its protection level", so i can't compile to check it yet. Is this what you were suggesting & any ideas on the Protection level issue??
Strings file:
public static class Strings
{
public static string UserDisplayName;
}
ViewModel:
namespace Travel_Comp.ViewModels
{
public sealed class MenuViewModel : INotifyPropertyChanged
{
//Azure sync process
ServerManager manager;
public event PropertyChangedEventHandler PropertyChanged;
public MenuViewModel()
{
//Initial set of UserDisplayNm
this.UserDisplayNm = Strings.UserDisplayName;
}
async void LoadAsync()
{
try
{
//Run process to populate Strings.UserDisplayNm, set Syncitems to true to sync with Server
foreach (var user in await manager.GetUsersAsync(syncItems: true))
{
Strings.UserDisplayName = user.FirstName;
}
}
catch (Exception e)
{
Console.WriteLine($"Error while retrieving user name: {e}");
}
}
public string UserDisplayNm
{
set
{
if (Strings.UserDisplayName != value)
{
value = Strings.UserDisplayName;
if (PropertyChanged != null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserDisplayNm)));
}
}
}
get
{
return "Welcome, " + Strings.UserDisplayName;
}
}
}
}
View:
protected override async void OnAppearing()
{
base.OnAppearing();
ViewModels.MenuViewModel.LoadAsync();
}
So if you're looking some guidance for MVVM, you should know that usually you put your dependencies in your view model constructor, here your Azure service.
Also you could use a existing MVVM framework that will make things easy for you, like Prism or FreshMVVM.
But if you want to go for full vanilla you can also call your vm code from the view code behind.
So I'm suggesting this modification to your MenuViewModel:
private IAzureService _azureService;
private string _userDisplayNm;
public MenuViewModel(IAzureService azureService)
{
_azureService = azureService;
}
public string UserDisplayNm
{
get
{
return _userDisplayNm;
}
set
{
if (_userDisplayNm != value)
{
_userDisplayNm = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserDisplayNm)));
}
}
}
public async void LoadAsync()
{
try
{
UserDisplayNm = await _azureService.GetUserName();
}
catch (Exception exception)
{
Debug.WriteLine($"Error while retrieving user name: {exception}")
}
}
Then in you view code behind:
void OnAppearing()
{
_menuViewModel.LoadAsync();
}
To resolve the question: Inaccessible due to its protection level, you can try to add the public access modifier before the function of LoadAsync.
public async void LoadAsync(){
//....
}
And I have created a simple demo to simulate your code.
The main code is:
public sealed class TestModel: INotifyPropertyChanged
{
//*******************************************
string _userDisplayName;
public string UserDisplayName {
set { SetProperty(ref _userDisplayName, value); }
get { return _userDisplayName; }
}
public async void LoadAsync()
{
try
{
UserDisplayName = "updated value: Angela";
Strings.UserDisplayName = UserDisplayName;
}
catch (Exception exception)
{
Debug.WriteLine($"Error while retrieving user name: {exception}");
}
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
xaml
<Label Text="{Binding UserDisplayName }" BackgroundColor="Yellow"
VerticalOptions="Center" HorizontalOptions="Fill" HeightRequest="50" />
<Button Text="update the Label value" Clicked="Button_Clicked"/>
And use like this:
public partial class MyPage1 : ContentPage
{
TestModel model;
public MyPage1 ()
{
InitializeComponent ();
model = new TestModel();
BindingContext = model;
}
private void Button_Clicked(object sender, EventArgs e)
{
model.LoadAsync();
}
}
The effect is:

File Browser Dialog MVVM C#, return complete path to view model and diplay only file name in view

I have a dialog window and I need to return a complete file path to my viewmodel and I would like to do this but show only file name in text box in view for user. I have a behaviors class and a viewmodel and a view. I tried catching the name as a property in the behaviors class but I am not getting the value when I bind to a text box to check for output. Here is the browser dialog
namespace BasemapCreator.Behaviors
{
public class FolderDialogBehavior:Behavior<System.Windows.Controls.Button>
{
public void FolderBrowserDialog()
{
}
public string SetterName { get; set; }
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += OnClick;
}
protected override void OnDetaching()
{
AssociatedObject.Click -= OnClick;
}
private void OnClick(object sender, RoutedEventArgs e)
{
var dialog = new FolderBrowserDialog();
var result = dialog.ShowDialog();
if (result == DialogResult.OK && AssociatedObject.DataContext != null)
{
var propertyInfo = AssociatedObject.DataContext.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
.Where(p => p.CanRead && p.CanWrite)
.First(p => p.Name.Equals(SetterName));
string dirName = new DirectoryInfo(dialog.SelectedPath).Name;
FolderName = dirName;
_fileName = System.IO.Path.GetFileName(dirName);
FileName = _fileName;
ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(FileName.ToString());
propertyInfo.SetValue(AssociatedObject.DataContext, dialog.SelectedPath, null);
}
}
private string _folderName;
public string FolderName
{
get { return _folderName; }
set
{
if (value != _folderName )
{
_folderName = value;
OnPropertyChanged("FolderName");
}
}
}
private string _fileName;
public string FileName
{
get { return _fileName; }
set
{
if (_fileName != value)
{
_fileName = value;
OnPropertyChanged("FileName");
}
}
}
private string _styleName;
public string StyleName
{
get { return _styleName; }
set
{
if (_styleName != value)
{
_styleName = value;
OnPropertyChanged("StyleName");
}
}
}
private void OnPropertyChanged(string propertyname)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyname));
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs args)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, args);
}
}
}
Here is the Binding in the View
<TextBox x:Name="gdbName" HorizontalAlignment="Left" Height="30" Margin="56,29,0,0" Text="{Binding FolderName, Mode=TwoWay}" VerticalAlignment="Top" Width="282" AllowDrop="True">
</TextBox>
And in the ViewModel I have the FolderName property
private string _folderName;
public string FolderName
{
get { return _folderName; }
set
{
_folderName = value;
OnPropertyChanged("FolderName");
}
}
everything works but the pathnames are so long that sometimes they are hidden. I would like to show file name and pass complete path.
It's not possible or at least very complicated if you display a shortened path or filename but let the user enter a full path…
I would use two different fields or use a OneWay Binding and let the user only select a new file with the FileOpenDialog but not let him enter a path manually…
So you create another property in your view model like:
public string SortenedFolderName => Path.GetFileName(_folderName);
private string _folderName;
public string FolderName
{
get { return _folderName; }
set
{
_folderName = value;
OnPropertyChanged(nameof(FolderName));
OnPropertyChanged(nameof(SortenedFolderName));
}
}
and bind to this property like:
<TextBox Text="{Binding SortenedFolderName, Mode=OneWay}" IsReadOnly="True" />

Background Worker Call Method or something

I have a method/procedure which works well, however it takes ages to do its thing so I want to move it into a background worker so people can still use the app.
Here is the code. (I cut down as much as I could)
public partial class NetworkInformation : UserControl, INotifyPropertyChanged
{
public NetworkInformation()
{
InitializeComponent();
Discovery();
}
public void Discovery()
{
GetIcon Icon = new GetIcon();
BitmapImage IconOfComputer = null;
List<DiscoveredComputer> NetworkedComputers = new List<DiscoveredComputer>();
DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup");
BitmapImage On = Icon.LoadIcon(#"/Images/Icons/ComputerOn.ico");
BitmapImage Off = Icon.LoadIcon(#"/Images/Icons/ComputerOff.ico");
foreach (DirectoryEntry Node in Discover.Children)
{
try
{
if (Node.Properties.Count > 0)
{
IconOfComputer = On;
}
}
catch
{
IconOfComputer = Off;
}
if (Node.Name != "Schema") { NetworkedComputers.Add(new DiscoveredComputer { Image = IconOfComputer, ComputerName = Node.Name, MyToolTip = "Node Type = " + Node.SchemaEntry.Name }); }
}
ListView_LocalComputers.ItemsSource = NetworkedComputers;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public class DiscoveredComputer : INotifyPropertyChanged
{
private string _ComputerName;
public string ComputerName
{
get { return _ComputerName; }
set
{
_ComputerName = value;
this.NotifyPropertyChanged("ComputerName");
}
}
private BitmapImage _Image;
public BitmapImage Image {
get { return _Image; }
set
{
_Image = value;
this.NotifyPropertyChanged("Image");
}
}
private String _MyToolTip;
public String MyToolTip
{
get { return _MyToolTip; }
set
{
_MyToolTip = value;
this.NotifyPropertyChanged("ToolTip");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public class GetIcon
{
public BitmapImage IconStorage { get; set; }
public BitmapImage LoadIcon(String IconPath)
{
BitmapImage GeneratedIcon = new BitmapImage();
GeneratedIcon.BeginInit();
GeneratedIcon.UriSource = new Uri("pack://application:,,," + IconPath, UriKind.RelativeOrAbsolute);
GeneratedIcon.EndInit();
IconStorage = GeneratedIcon;
return GeneratedIcon;
}
}
}
This all works awesomely, somehow...
Here is the code I:developed for my background worker
public partial class MyBackgroundWorker : UserControl
{
WorkerData BGW;
public MyBackgroundWorker()
{
InitializeComponent();
BGW = new WorkerData();
#region Workers Events
BGW.ThisWorker.DoWork += new DoWorkEventHandler(Workers_DoWork);
BGW.ThisWorker.ProgressChanged += new ProgressChangedEventHandler(Workers_Progress);
BGW.ThisWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Workers_Completed);
BGW.ThisWorker.WorkerReportsProgress = true;
BGW.ThisWorker.WorkerSupportsCancellation = true;
#endregion
}
public void RibbonButton_EventClickStart(object sender, RoutedEventArgs e)
{
BGW.ThisWorker.RunWorkerAsync();
}
public void UserForm_Loaded(object sender, RoutedEventArgs e)
{
}
public void RibbonButton_EventClick(object sender, RoutedEventArgs e)
{
BGW.ThisWorker.CancelAsync();
}
public void Workers_DoWork(object sender, DoWorkEventArgs e)
{
}
public void Workers_Progress(object sender, ProgressChangedEventArgs e)
{
BGW.ThisWorkersProgress = e.ProgressPercentage;
}
public void Workers_Completed(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) { BGW.ThisWorkersResult = "Cancelled By User"; }
else if (e.Error != null) { BGW.ThisWorkersResult = "Error Encountered: " + e.Error.Message; }
else
{
BGW.ThisWorkersResult = "Task Completed Successfully";
BGW.WorkersReturnObject = e.Result;
}
}
}
public class WorkerData
{
public BackgroundWorker ThisWorker { get; set; }
public int ThisWorkersProgress { get; set; }
public string ThisWorkersResult { get; set; }
public object WorkersReturnObject { get; set; }
public object ThisWorkersJob { get; set; }
public WorkerData()
{
ThisWorker = new BackgroundWorker();
}
}
So how do I get my background worker to run the Discovery method I have created?
You need to do your work in the DoWork event handler.
I don't know if you need a whole separate class for this. I prefer to create these as I need them, on the fly. I think you'll get yourself shoehorned, where you'll use your class in multiple places and then decide you want to do something else in Workers_Completed in certain cases, or do something different when an error occurs in certain cases, and that one class could end up being a tangled-up pain. That's just my opinion though.
Also, you have to be very careful about touching the UI thread from your BackgroundWorker. In the example below, I'm passing in your node count to the DoWork event, instead of having it possibly touch a UI component directly. I'm also passing the list to the RunWorkerCompleted event, so that you're back in the main thread when it tries to attach the list to your ListView.
var bw = new BackgroundWorker();
bw.DoWork += (s, e) =>
{
var nodePropertiesCount = (int)e.Argument;
// the guts of `Discovery` go in here
e.Result = NetworkedComputers;
};
bw.RunWorkerCompleted += (s, e) =>
{
if (e.Error != null)
{
// Task Completed Successfully
ListView_LocalComputers = (List<DiscoveredComputer>)e.Result;
}
else
{
// Error Encountered
}
};
bw.RunWorkerAsync(Node.Properties.Count);
SLaks answer is correct, but you apparently don't understand what that means. I'd suggest taking the guts of Discover() and putting them in the Workers_DoWork() method like this:
public void Workers_DoWork(object sender, DoWorkEventArgs e)
{
var backgroundWorker = sender as BackgroundWorker;
GetIcon Icon = new GetIcon();
BitmapImage IconOfComputer = null;
List<DiscoveredComputer> NetworkedComputers = new List<DiscoveredComputer>();
DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup");
BitmapImage On = Icon.LoadIcon(#"/Images/Icons/ComputerOn.ico");
BitmapImage Off = Icon.LoadIcon(#"/Images/Icons/ComputerOff.ico");
while (!backgroundWorker.CancellationPending)
{
foreach (DirectoryEntry Node in Discover.Children)
{
try
{
if (Node.Properties.Count > 0)
{
IconOfComputer = On;
}
}
catch
{
IconOfComputer = Off;
}
if (Node.Name != "Schema") { NetworkedComputers.Add(new DiscoveredComputer { Image = IconOfComputer, ComputerName = Node.Name, MyToolTip = "Node Type = " + Node.SchemaEntry.Name }); }
}
break;
}
if(backgroundWorker.CancellationPending)
{
e.Cancel = true;
}
else
{
e.Result = NetworkedComputers;
}
}
And then modifying your Workers_Completed() like this:
public void Workers_Completed(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) { BGW.ThisWorkersResult = "Cancelled By User"; }
else if (e.Error != null) { BGW.ThisWorkersResult = "Error Encountered: " + e.Error.Message; }
else
{
BGW.ThisWorkersResult = "Task Completed Successfully";
//BGW.WorkersReturnObject = e.Result;
//background worker can't touch UI components
ListView_LocalComputers.ItemsSource = e.Result as List<DiscoveredComputer>;
}
}
I suggest these changes, or something similar, because the background worker can't modify/access UI components (like your ListView), so it has to pass back the value to use for the ListView view its Result property. I also included a simple way of detecting cancellation; I'll leave progress reporting up to you to implement.

Something wrong with xml serialization/deserialization

I'm dipping my feet into Windows Phone development and are starting to get to terms with some of the features of Silverlight on WP, but I'm struggling with XML:
I'm trying to serialize some objects into an XML and then read the said XML and serialize it into objects again. Then I'll use that to populate a listbox by putting an ObservableCollection as the ItemsSource of the listbox.
I've already made sure that the databinding works properly; if I just generate the objects and put them into an Observable Collection and then put that as the ItemsSource, there are no problems. It would seem that it's the XML part of my code that's faulting. Everything compiles and executes nice enough, but the listbox remains empty :(
This code executes as the app launches (not very effective but works for my testing):
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
ObservableCollection<Quote> quotes = new ObservableCollection<Quote>();
for (int i = 0; i < 10; i++)
{
Quote quote = new Quote()
{
Author = "Author #" + i.ToString(),
QuoteText = "This is quote #" + i.ToString(),
};
quotes.Add(quote);
}
XmlWriterSettings xmlwrtrstngs = new XmlWriterSettings();
xmlwrtrstngs.Indent = true;
using(IsolatedStorageFile isostrg = IsolatedStorageFile.GetUserStoreForApplication())
{
using(IsolatedStorageFileStream isoflstrm = isostrg.OpenFile("Quotes.xml", FileMode.Create))
{
XmlSerializer xmlsrlzr = new XmlSerializer(typeof(QuoteCollection));
using(XmlWriter xmlwrtr = XmlWriter.Create(isoflstrm, xmlwrtrstngs))
{
xmlsrlzr.Serialize(xmlwrtr, quoteCollection);
}
}
}
loadData();
}
void loadData()
{
try
{
using(IsolatedStorageFile isostrg = IsolatedStorageFile.GetUserStoreForApplication())
{
using(IsolatedStorageFileStream isoflstrm = isostrg.OpenFile("Quotes.xml", FileMode.Open))
{
XmlSerializer xmlsrlzr = new XmlSerializer(typeof(QuoteCollection));
QuoteCollection quoteCollectionFromXML = (QuoteCollection)xmlsrlzr.Deserialize(isoflstrm);
LstBx.ItemsSource = quoteCollectionFromXML.Quotes;
}
}
}
catch(Exception)
{
Console.Write("Something went wrong with the XML!");
}
}
QuoteCollection
public class QuoteCollection : INotifyPropertyChanged
{
ObservableCollection<Quote> quotes;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Quote> Quotes
{
get { return quotes; }
set
{
if(quotes != value)
{
quotes = value;
raisePropertyChanged("Quotes");
}
}
}
protected virtual void raisePropertyChanged(string argPropertyChanged)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(argPropertyChanged));
}
}
}
Quote
public class Quote : INotifyPropertyChanged
{
string author;
string quoteText;
public event PropertyChangedEventHandler PropertyChanged;
public string Author
{
get
{
return author;
}
set
{
if(author != value)
{
author = value;
onPropertyChanged("Author");
}
}
}
public string QuoteText
{
get
{
return quoteText;
}
set
{
if(quoteText != value)
{
quoteText = value;
onPropertyChanged("QuoteText");
}
}
}
protected virtual void onPropertyChanged(string argProperty)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(argProperty));
}
}
}
Any insight would be most appreciated :)
You are serializing using QuoteCollection as the type, but actually writing out an ObservableCollection<Quote>.
Forgot to put the ObservableCollection into the QuoteCollection instance I'd made previously (not shown in the code posted).

C# Custom Class using INotifyPropertyChanged not notifying on Assignment

I have a class that implements the INotifyPropertyChanged interface.
If I create a new instance of the object, the PropertyChanged event gets set after I retrieve a value from it.
Example:
MyItem itm = new MyItem(); //MyItem.PropertyChanged == null
string test = itm.Value; //MyItem.PropertyChanged != null
If I assign itm the value of another MyItem, the PropertyChanged event remains null.
Example:
itm = (MyItem)cboMyItemsCombobox.SelectedItem; // Properties for itm change to the values
// of the selected item, but PropertyChanged
// == null
I believe the problem lies partially in my custom constructor for the class, but I'm not entirely sure.
The goal is to have a variable to hold data for a record, called mnuitm, that is bound to
3 textbox objects. When the text in a textbox changes, the change is made to the property in mnuitm. When the property in mnuitm is changed, the change is made in the textbox.
This works if I create a new MenuItem and assign the values individually, but does not work if I assign an already populated MenuItem to mnuitm.
Here is my actual code for (hopefully) more clearity on the issue.
public partial class frmMenuItems : Form
{
private class MenuItem : INotifyPropertyChanged
{
private Int32 mid;
private string txt;
private string url;
private string scp;
public MenuItem() { }
public MenuItem(Int32 id, string txt, string url, string scp)
{
ID = id;
Text = txt;
URL = url;
Script = scp;
}
public Int32 ID
{
get
{
return mid;
}
set
{
if (mid != value)
{
mid = value;
NotifyPropertyChanged("ID");
}
}
}
public string Text {
get
{
return txt;
}
set
{
if (txt != value)
{
txt = value;
NotifyPropertyChanged("Text");
}
}
}
public string URL {
get
{
return url;
}
set
{
if (url != value)
{
url = value;
NotifyPropertyChanged("URL");
}
}
}
public string Script {
get
{
return scp;
}
set
{
if (scp != value)
{
scp = value;
NotifyPropertyChanged("Script");
}
}
}
public void Clear()
{
ID = 0;
Text = "";
URL = "";
Script = "";
}
public override string ToString()
{
return Text;
}
private void NotifyPropertyChanged(string inf)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(inf));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
private MenuItem mnuitm;
private MySqlConnection sqlcon;
public frmMenuItems()
{
InitializeComponent();
}
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
}
private void btnCancel_Click(object sender, EventArgs e)
{
mnuitm.Clear();
}
private void frmMenuItems_Load(object sender, EventArgs e)
{
string constr = "server={0};uid={1};pwd={2};database={3};";
DBItem dbi = CountyDataManager.CountyData.DBConnection;
constr = string.Format(constr, [MyHost], [MyUsername], [MyPassword], [MyDatabase]);
sqlcon = new MySqlConnection(constr);
sqlcon.Open();
mnuitm = new MenuItem();
SetBindings();
RefreshList();
}
private void SetBindings()
{
txtMenuText.DataBindings.Clear();
txtURL.DataBindings.Clear();
txtScript.DataBindings.Clear();
txtMenuText.DataBindings.Add("Text", mnuitm, "Text");
txtURL.DataBindings.Add("Text", mnuitm, "URL");
txtScript.DataBindings.Add("Text", mnuitm, "Script");
}
private void RefreshList()
{
using (MySqlCommand cmd = new MySqlCommand("SELECT `menuid`,`menutext`,`url`,`script` FROM tblindexmenu ORDER BY `menutext`", sqlcon))
{
lstMenuItems.Items.Clear();
using (MySqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
lstMenuItems.Items.Add(new MenuItem(Int32.Parse(rdr[0].ToString()), rdr[1].ToString(),rdr[2].ToString(),rdr[3].ToString()));
}
}
}
}
private void frmMenuItems_FormClosing(object sender, FormClosingEventArgs e)
{
sqlcon.Close();
}
private void lstMenuItems_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstMenuItems.SelectedIndex > -1)
{
mnuitm = (MenuItem)lstMenuItems.SelectedItem;
}
}
}
After receiving feedback, I made the following changes:
Added CopyFrom() to MenuItem
public void CopyFrom(MenuItem itm)
{
this.ID = itm.ID;
this.URL = itm.URL;
this.Text = itm.Text;
this.Script = itm.Script;
}
I then modified the SelectedIndexChanged code to use the new function
mnuitm.CopyFrom((MenuItem)lstMenuItems.SelectedItem);
This is by design. When you write
itm = (MyItem)cboMyItemsCombobox.SelectedItem;
you haven't changed any of the properties of the MenuItem itm used to point to, rather you changed the MenuItem itm points to.
One option for what you need is add a function to MenuItem that looks like
SetFromOtherMenuItem(MenuItem other)
{
this.Url = other.Url
//etc
}
Again, PropertyChanged means that a property on some instance has changed. In your case, only one of the variables pointing to that instance changed (to point to a different instance).

Categories

Resources