Application with built-in database handle updates without losing data

  • (2 Pages)
  • +
  • 1
  • 2

23 Replies - 891 Views - Last Post: 06 March 2019 - 07:37 AM Rate Topic: -----

#16 danbywinby   User is offline

  • D.I.C Regular

Reputation: 3
  • View blog
  • Posts: 305
  • Joined: 06-January 13

Re: Application with built-in database handle updates without losing data

Posted 04 February 2019 - 01:35 AM

Sorry about that. I didn't realise that bit wasn't clear.

I'm pretty sure that i did exclude the file to start with but then i had the issue where the program couldn't find the file because it wasn't getting built and put in the correct location.

You are right that it probably does seem odd about distributing an application without a website maybe once i have this built i will change my mind haha.

I will agree that a self-updating application probably would be the best way to go. You say you are happy to help me build one. Would you be able to put together an example one at all so that i can see exactly how it would work and try and build one myself?
Was This Post Helpful? 0
  • +
  • -

#17 Sheepings   User is offline

  • Senior Programmer
  • member icon

Reputation: 203
  • View blog
  • Posts: 1,129
  • Joined: 05-December 13

Re: Application with built-in database handle updates without losing data

Posted 04 February 2019 - 09:00 PM

A self updating application is the way to go. Building it any other way is just sh!t and a turn-off especially if you will be rolling out any updates. What I will do is help you build an updater, and give you the tips needed, and a push in the right direction, of course you will be using MVVM, and this is something you will need to implement as I currently have my hands full developing a client's game in Unity3D.

So this is what I've created so far, but I won't be giving you all of this, because I want you to show commitment too. I love to see people who are participating in the learning experience and engaging with the information being provided to them, and showing commitment at the same time. So I am not going to do it all for you, but I will guide you and help you with any questions. In the end, you will have something like this tested and working ::

Attached Image

I've put this together in the last 25 minutes, and it still needs a lot of work. It is not provided as a perfect example but it does provide you with the methods required to make it work as an updater. Your job will be fixing it up and putting the finishing touches on it. So to start out, you will want to apply some decent structuring to your Updater Project, so lets start with the classes. Shall we... Your main ::
        public static MainWindow MW; //Allows us to directly access Main Window from other classes.
        public MainWindow()
        {
            InitializeComponent();
            MW = this;
            var oRef = new StartUpdater();
            oRef.SetValues();
        }

Sometimes I like to get under the hood, and have access to a static declaration for your main window as it can prove useful when working with classes. Next, you might want a handler for your webClient download progress, we will get back to this bit of code later ::
public void wClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            pgBar.Value = e.ProgressPercentage;
            StatusLabel.Content = "Downloading new version, version " + Variables.AvailableVersion + " :: at " + e.ProgressPercentage.ToString() + "% Completed";
            if (e.ProgressPercentage == 100) { pgBar.Visibility = Visibility.Hidden; }
        }
But it belongs under your MainWindow() in your partial class, you can setup the handler a bit later.

Since I like to compartmentalize my code, I have created a separate class declaring my variables static inside it. ::
    public class Variables
    {
        public static readonly string AppFileName = "MyMainApp.exe"; //Change this to the name of your main app
        public static readonly string txtFileVersionURL = "https://textuploader.com/1ab7b"; //Best upload a text doc to your web server with only the version numbers, and clean out the GetAvailVer method.
        public static readonly string newVersionURI = "https://yourwebsitehere.com/downloads/MyMainApp.exe"; //You need to set your own URL here and you will be able to download your new file version. I've removed my domain name.
        public static string filePath = Directory.GetCurrentDirectory(); //Gets the direcory if the file running. Your updater will be in the same folder as your main app.
        public static readonly string newFileWithPath = (Path.Combine(filePath, AppFileName)); //Combines the path with the file name
        public static string CurrentVersion; //Current cersion of app is held here.
        public static string AvailableVersion; //Available version of app is held here.
    }
Another class for starting the updater ::
    public class StartUpdater
    {        
        internal void SetValues()
        {
            var ORef = new Function(); //Create an obkect reference for the Function class.
            string curVer = ORef.GetProductVersion(); //Get the current version of your application.
            //Next we get the available version of your application by downloading a text file with your version numbers. I had to use a web page, but its an easy adjustment.
            string availVer = ORef.GetAvailVersion(Variables.txtFileVersionURL); 
            var oRefUI = new UpdateUI(); //Create an object reference for the UpdateUI class.
            //Next we will create a thread, pass it an anonomous delegate method to pass in the object reference to the UpdateUI UPDATE method, along with the required parameters.
            Thread thread = new Thread(delegate() { oRefUI.UPDATE(oRefUI.CurrentVersion, oRefUI.AvailableVersion, true, "Checking for new versions..."); }); //See above ^^/>
            thread.Start(); 
        }
    }
We then create a function class and add some methods to it ::
    public class Function
    {
        internal string GetProductVersion()
        {
            var oRef = new UpdateUI(); //Create an object Reference to the Variables Class so we can get the name of the file
            var fPath = System.IO.Directory.GetCurrentDirectory(); //Be sure to build your application in the same direcoty as your updater or you will get an exception. This gets current direcory.
            var pathCombine = Path.Combine(fPath, Variables.AppFileName); //Combine the path and the file name to make a complete path including the file name.
            var v = Assembly.LoadFile(pathCombine).GetName().Version.ToString(); //Load the path of the file, and acquire the location from pathCombined, and acquire the version to string format.
            
            Console.WriteLine(fPath);
            Console.WriteLine(pathCombine);
            Console.WriteLine(v);

            oRef.CurrentVersion = v;

             //Set the path to the current version as static so you can set it without object ref
            return v;
        }

        internal string GetAvailVersion(string onlineTxtFile)
        {
            var wClient = new WebClient(); //Create an instance of the WebCient.
            // Next, download the string of the URL, this should be a direct link to your text file and not a html file. But this is how it had to be done for the example.
            string myResponse = wClient.DownloadString(onlineTxtFile); //See above ^^/>
            Mainwindow.MW.pgBar.Value = 5; //This sets the progress bar through our sneaky static MW declartation in our main partial class. 
            //The DownloadString method downloads the URL contents, and so given I was working with a URL and not a text file with only version numbers, we need to clean up...
            var subStr = myResponse.Substring(8654); //There are 8654 chars in the source code up until the version numbers are reached, and we need to remove them using substring.
            var cleanedResponse = subStr.Remove(7); //We then remove the last remaining chars from the substring leaving only the version numbers. Version numbers consist of 7 digits. 
            // NOTE :: if you use a direct link to an online text file, with only the version numbers, download string myResponse will do to return only version numbers. 
            Console.WriteLine(cleanedResponse); //Cleaned up the response and this outputs your version number only.
            return cleanedResponse;
        }
        public bool DeleteFile(string fName)
        {
            if (File.Exists(fName)) //Check if the file of your main app exists, and if true, we delete it and download the new one. 
            {
                try
                {
                    File.SetAttributes(fName, FileAttributes.Normal); //Set default attributes first.
                    File.Delete(fName); //Delete the file
                    return true;
                }
                catch (Exception ex) 
                {
                    Console.WriteLine("File not deleted; error is :: " + ex.StackTrace);
                    return false;
                //Handle what to do next
                }
            }
            return false;
        }
    }    

Next you need to work on the UpdateUI class, as I've plucked some bits out of it so that you can put in some of the effort and finish it. And its going to need some touching up as it may have some minor issues compared to the working screenshot I provided. The class looks like this ::
public class UpdateUI
    {   //Because this method was started from another thread, we don't own it, and so we can't access controls from here without delegating and invoking. 
        public void UPDATE(string CurrentVersion, string AvailableVersion, bool seeProgress, string Status)
        {
            Application.Current.Dispatcher.Invoke(delegate //Use this delegate to put yourself back on talking term with your UI by invoking the controls to set them. 
            {
                //This used the Current Version we passed in from UpdateUI.CurrentVersion in the threat we started and passed the parameters. Otherwise, If I wanted to use the below, I'd use this.CurrentVersion. 
                Mainwindow.MW.AppVersionNum_Label.Content = CurrentVersion; //See ^^/>
                Mainwindow.MW.AvailAppVersionNum_Label.Content = AvailableVersion; //Same as above ^^/>
                Mainwindow.MW.StatusLabel.Content = Status; //Gives the status label a status through our static friend in the partial main method.
                //Something to point out, we are in a new thread and operating off the UI, and so if you move the above three lines outside the dispatcher, you will get thread ownership exception. (Paraphraising) 
                if (CurrentVersion != AvailableVersion)
                {
                    if (seeProgress == true) { Mainwindow.MW.pgBar.Visibility = Visibility.Visible; } //Since we are updating the UI, as there are new versions, we will show the progress bar. 
                    //You could also do some fancy timer stuff per tick and set the opacity of the pgBar to 0 and increase it for a fade in effect. ;)/>
                    Console.WriteLine("Download new version to application directory.");
                    Mainwindow.MW.StatusLabel.Content = "A new version is available. Downloading version " + AvailableVersion + ", please wait..."; //Just updating the status label to keep user informed.
                    //You should set some bool or a step process from this point to move to the next update method. 
                    //You will from here finish off the update process yourself by following some todo tips after the code box...
                }
            });
        }
        public string CurrentVersion
        {
            get
            {
                return Variables.CurrentVersion;
            }
            set
            {
                Variables.CurrentVersion = value;
            }
        }
        public string AvailableVersion
        {
            get
            {
                return Variables.AvailableVersion;
            }
            set
            {
                Variables.AvailableVersion = value;
            }
        }
    }


TO DO ::

Check that your application is open and close it if it is ::
Process[] processes = Process.GetProcessesByName("MainWindow"); //We need to check if your application is open, and close it.
                    if (processes.Length > 0)
                    {
                        processes[0].CloseMainWindow(); //Bye bye
                    }
Create an object reference for the Function class and then delete your main application file once you know the process is closed. ::
var oRef = new Function(); //Create an object reference to the Function Class.
                    oRef.DeleteFile(Variables.newFileWithPath); //Delete the main application file.
Next you will want to make use of that Web Client to download the new file to your application directory. And while its downloading you could use something like this expression to increase the progress made on your progress bar ::
// wClient.DownloadFile(Variables.newVersionURI, Variables.AppFileName);
                    wClient.DownloadProgressChanged += (a, events) =>
                    {
                        Mainwindow.MW.pgBar.Value = events.ProgressPercentage;
                    };
You also need to add more Get/Setters for your UI labels such as the status label and any other you might want to add around the pgBar.

Once your application has downloaded, I suggest checking that it has fully downloaded and then launch it if the complete file is ready to be launched. You could also do an MD5 check of the file once downloaded. I believe you don't need to dispose of MD5, but I'd probably just wrap it in using blocks anyway just in case I am wrong on that one.

Rename UPDATE void to UPDATEONE

For UPDATETWO - Add the ability to close the open application and do these following thread calls from the UI thread, or you will have complications, and you be trying to use Task to get you back on the UI thread again when trying to delete a file etc, unless your current non-UI thread ends...

For UPDATETHREE - Add the ability to delete the file you just closed in UPDATETWO. Again, make sure you are on the UI thread when doing this.

For UPDATEFOUR - Add the ability to download the new file from your web server using a web client. And then check if the file is completely downloaded in full. And if it is, proceed to the last step.

For UPDATEFIVE - Add the ability for your application to launch the new file you downloaded to your current directory, and providing the new file launches, exit the updater immediately upon opening your new version of your updated application.

NOTE :: You should execute the above updater methods from the StartUpdater Class in the SetValues() method just after the thread.Start(); in the order of one to five and you will then have a fully working updater.

One last and final thing you will need, especially if you want to delete files in your app folder, and that is elevated permissions for your application to run as admin (which I feel you may need). This is a rather simple step; where you will add to your application updater project, a app.manifest file. And it will look like this when you add it ::
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <!-- UAC Manifest Options
            If you want to change the Windows User Account Control level replace the 
            requestedExecutionLevel node with one of the following.

        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />

            Specifying requestedExecutionLevel node will disable file and registry virtualization.
            If you want to utilize File and Registry Virtualization for backward 
            compatibility then delete the requestedExecutionLevel node.
        -->
        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>

  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- A list of all Windows versions that this application is designed to work with. 
      Windows will automatically select the most compatible environment.-->

      <!-- If your application is designed to work with Windows Vista, uncomment the following supportedOS node-->
      <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"></supportedOS>-->

      <!-- If your application is designed to work with Windows 7, uncomment the following supportedOS node-->
      <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>-->

      <!-- If your application is designed to work with Windows 8, uncomment the following supportedOS node-->
      <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS>-->

    </application>
  </compatibility>

  <!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
  <!-- <dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="*"
          publicKeyToken="6595b64144ccf1df"
          language="*"
        />
    </dependentAssembly>
  </dependency>-->

</asmv1:assembly>

Be sure to change this line ::
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
to this line ::
<requestedExecutionLevel  level="highestAvailable" uiAccess="false" />
And last of all, here is the XAML i used just in case you want it ::
<Window x:Name="MyAppUpdater" x:Class="Application_Updater.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="My App Updater : Checking" Height="409" Width="666">
    <Grid x:Name="MainWindowGrid">
        <Label x:Name="TitleLabel" Content="MY UPDATER APPLICATION" HorizontalAlignment="Left" Margin="200,44,0,0" VerticalAlignment="Top" FontSize="20" Foreground="#FF0B90FF"/>
        <Label x:Name="AppVersionLabel_PlaceHolder" Content="Application Version" HorizontalAlignment="Left" Margin="77,139,0,0" VerticalAlignment="Top" FontSize="16"/>
        <Label x:Name="AppVersionNum_Label" Content=":" HorizontalAlignment="Left" Margin="309,139,0,0" VerticalAlignment="Top" FontSize="16"/>
        <Label x:Name="AvailableVersionLabel_Placeholder" Content="Available Version" HorizontalAlignment="Left" Margin="77,253,0,0" VerticalAlignment="Top" FontSize="16"/>
        <Label x:Name="AvailAppVersionNum_Label" Content=":" HorizontalAlignment="Left" Margin="309,253,0,0" VerticalAlignment="Top" FontSize="16"/>
        <ProgressBar x:Name="pgBar" HorizontalAlignment="Left" Height="15" Margin="77,331,0,0" VerticalAlignment="Top" Width="500" Background="#D8E6E6E6" Visibility="Hidden"/>
        <Label x:Name="StatusLabel" Content=":" HorizontalAlignment="Left" Margin="77,300,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.237,-0.308" FontSize="16" FontStyle="Italic"/>

    </Grid>
</Window>



Post back your changes, and if you get stuck, I will try to help you out if I have the time. But as I said, I won't do it all for you, and so; you need to put in some effort too. As it stands now, minus some of the guts I pulled out, it does work and you already have the important parts of what you need to finish the rest, minus a few edits easy peasy.

Lets know if this helped you.
Was This Post Helpful? 0
  • +
  • -

#18 Sheepings   User is offline

  • Senior Programmer
  • member icon

Reputation: 203
  • View blog
  • Posts: 1,129
  • Joined: 05-December 13

Re: Application with built-in database handle updates without losing data

Posted 04 February 2019 - 09:20 PM

Also, one other thing that I left out since mentioning MVVM, I'd highly recommend this MVVM post by Josh Smith of MSDN if you are interested in learning more on MVVM. A highly recommended read, and may be helpful to you while dabbling with WPF.

Good night!
Was This Post Helpful? 0
  • +
  • -

#19 danbywinby   User is offline

  • D.I.C Regular

Reputation: 3
  • View blog
  • Posts: 305
  • Joined: 06-January 13

Re: Application with built-in database handle updates without losing data

Posted 05 February 2019 - 02:20 PM

Thank you very much for this.

I have made a start on re-typing out in my own words all of the code you have posted into my own new project in Visual Studio.

I can already tell that this will take me while to get working but you have given me a great starting point and so i want to thank you very much for that.

I am going to need to find somewhere online where i can store the required files and i will also need to create a quick test app that i can use to test updating so that i can make sure this updater works.

I do have one question so far and that is where you mention that the app and the updater need to be built in the same directory. What exactly do you mean by that?
Was This Post Helpful? 0
  • +
  • -

#20 Sheepings   User is offline

  • Senior Programmer
  • member icon

Reputation: 203
  • View blog
  • Posts: 1,129
  • Joined: 05-December 13

Re: Application with built-in database handle updates without losing data

Posted 05 February 2019 - 03:08 PM

I do have one question so far and that is where you mention that the app and the updater need to be built in the same directory. What exactly do you mean by that?

Posted Image

You need to set your debug or build options to build the project in the same directory as the updater, or vice versa. When you distribute your application, your updater should be the program to launch first, and if your application does not need an update, it should launch your main programs executable and upon doing that, close itself. The way I designed the updater was with the intention of keeping the updater in the same directory as the main application, not just when debugging but at the distribution stage too. The gif shows what I mean. Remember to hit the green tick so other readers know what you found useful. And if you have any other questions, there is always someone online who can help you if I'm not about.

Also, it shouldn't take you any longer than 45 minutes to finish that updater. And when you do, post back your changed. I'd be interested to see how you went about it.
Was This Post Helpful? 0
  • +
  • -

#21 danbywinby   User is offline

  • D.I.C Regular

Reputation: 3
  • View blog
  • Posts: 305
  • Joined: 06-January 13

Re: Application with built-in database handle updates without losing data

Posted 05 March 2019 - 04:04 PM

Hi

I am still working on this slowly but surely.

However I am having issues at the moment with getting the threading to work. I've never done threading before apart from through using the backgroundworker so I'm probably missing something simple.

I have changed all of the variables to work with binding because I noticed that you weren't using binding at all.

But now whenever I try to access one of the variables from within the new thread I get an 'Object reference is required' error.
Was This Post Helpful? 0
  • +
  • -

#22 Sheepings   User is offline

  • Senior Programmer
  • member icon

Reputation: 203
  • View blog
  • Posts: 1,129
  • Joined: 05-December 13

Re: Application with built-in database handle updates without losing data

Posted 05 March 2019 - 05:54 PM

using System.Linq;

namespace TestCSharpApp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Start();
        }
        private void Start()
        {
            // Console.WriteLine(Var.IntVarI); //Var.IntVarI will not be accessible because we are calling it without creating an object reference and since its not static we can't call it like this Var.IntVarI, so lets create an object reference of the class
            var varobjRef = new Var(); //Object reference created
            Console.WriteLine("The number is :: " + varobjRef.IntVarI); //Use varobjRef as our object reference, and no more object reference error
            Console.WriteLine("The number is :: " + Var.IntVarII); //This is accessible because we are calling a static member which you can do without a object reference because its a static member. 
        }
    }
    public class Var
    {
        private int i = 5;
        private static int ii = 10;
        public int IntVarI
        {
            get
            {
                return this.i;
            }
            set
            {
                this.i = value;
            }
        }
        public static int IntVarII
        {
            get
            {
                return ii;
            }
            set
            {
                ii = value;
            }
        }
    }

}



The error explicitly tells you that you have not created a object reference for the inaccessible object. It looks like you are calling a non-static property from a static method. You'll need to either make the property static, or create an instance of the object you're trying to reach. So create a reference. The example above shows how to do this. You may also need to invoke your control/object if you are on another thread.

You'll need to use WPF's dispatcher to invoke the control/object by delegating. Please search Sheepings dispatcher on the dreamincode forums search utility, as I've answered this many timers before. If you are still stuck, post your code and explain your issue. Without seeing what you're doing, we have a case of blind leading the blind. The example above is based on the assumption of the description you gave.

I have changed all of the variables to work with binding because I noticed that you weren't using binding at all.

Of course. You need to do that, I'm not doing everything for you. I've already given you a good starter on post 17. And on post 18, I linked you a brilliant source for MVVM should you need it.

Let me know if you have any other questions and please remember to post your code.

Outputs ::
The number is :: 5
The number is :: 10
Was This Post Helpful? 0
  • +
  • -

#23 danbywinby   User is offline

  • D.I.C Regular

Reputation: 3
  • View blog
  • Posts: 305
  • Joined: 06-January 13

Re: Application with built-in database handle updates without losing data

Posted 06 March 2019 - 12:43 AM

Sorry was late last night and completely forgot that i should post my code.

Here is my MainViewModel so far (sorry for all of the comments but when chnaging bit's of your code i prefer to comment it out rather than delete so that i can refer back to it if i need to):
class MainViewModel : onpropertychanged
    {
        public MainModel mainModel;

        public MainViewModel()
        {
            mainModel = new MainModel();

            CurrentVersion = "0.0.0.1";
            AvailableVersion = "0.0.0.2";
            ProgressMessage = "Downloading........";
            ProgressValue = 10;

            //var oRef = new StartUpdater();
            //oRef.SetValues();
            StartUpdaterProcess();
        }

        #region Variables from Model
        public string CurrentVersion
        {
            get { return mainModel.CurrentVersion; }
            set
            {
                if (mainModel.CurrentVersion != value)
                {
                    mainModel.CurrentVersion = value;
                    onpropertychanged("CurrentVersion");
                }
            }
        }

        public string AvailableVersion
        {
            get { return mainModel.AvailableVersion; }
            set
            {
                if (mainModel.AvailableVersion != value)
                {
                    mainModel.AvailableVersion = value;
                    onpropertychanged("AvailableVersion");
                }
            }
        }

        public string ProgressMessage
        {
            get { return mainModel.ProgressMessage; }
            set
            {
                if (mainModel.ProgressMessage != value)
                {
                    mainModel.ProgressMessage = value;
                    onpropertychanged("ProgressMessage");
                }
            }
        }

        public int ProgressValue
        {
            get { return mainModel.ProgressValue; }
            set
            {
                if (mainModel.ProgressValue != value)
                {
                    mainModel.ProgressValue = value;
                    onpropertychanged("ProgressValue");
                }
            }
        }

        public bool IsBusyIndicator
        {
            get { return mainModel.IsBusyIndicator; }
            set
            {
                if (mainModel.IsBusyIndicator != value)
                {
                    mainModel.IsBusyIndicator = value;
                    onpropertychanged("IsBusyIndicator");
                }
            }
        }
        #endregion

        //public void wClient_DownloadProgressChnaged(object sender, DownloadProgressChangedEventArgs e)
        //{
        //    pgBar.Value = e.ProgressPercentage;
        //    StatusLabel.Content = "Downloading new version, version " + Variables.AvailableVersion + " :: at " + e.ProgressPercentage.ToString() + "% Completed";
        //    if (e.ProgressPercentage = 100)
        //    {
        //        pgBar.Visiblity = Visibility.Hidden;
        //    }
        //}

        private void StartUpdaterProcess()
        {
            var ORef = new Function(); // Create an object reference for the Function class
            //string curVer = ORef.GetProductVersion(); // Get the current version of your application
            CurrentVersion = ORef.GetProductVersion();

            // Next get the available version of your application by downloading a text file with your version numbers.
            //string availVer = ORef.GetAvailVersion(Variables.txtFileVersionUrl);
            AvailableVersion = ORef.GetAvailVersion(Variables.txtFileVersionUrl);

            var oRefUI = new UpdateUI(); // Create an object reference for the UpdateUI class.

            // Next we create a thread, pass it an anonymous delegate method to pass in the object reference to the UpdateUI UPDATE method, along with the required parameters
            //Thread thread = new Thread(delegate () { oRefUI.UPDATE(oRefUI.CurrentVersion, oRefUI.AvailableVersion, true, "Checking for new versions...."); });
            //Thread thread = new Thread(delegate () { oRefUI.UPDATE(CurrentVersion, AvailableVersion, true, "Checking for new versions...."); });
            //thread.Start();
            UPDATENEW(CurrentVersion, AvailableVersion, true, "Checking for new versions......");
        }

        public class Variables
        {
            public static readonly string AppFileName = "SelfUpdatingApplication.exe";
            public static readonly string txtFileVersionUrl = "http://192.168.1.122/TestVersion.txt"; //Best upload a text doc to your web server with only the version numbers, and clean out the GetAvailVer method.
            public static readonly string newVersionURI = ""; //You need to set your own URL here and you will be able to download your new file version.
            public static string filePath = Directory.GetCurrentDirectory(); //Gets the direcory if the file running. Your updater will be in the same folder as your main app.
            public static readonly string newFileWithPath = (Path.Combine(filePath, AppFileName)); //Combines the path with the file name
            //public static string CurrentVersion;
            //public static string AvailableVersion;
        }

        //public class StartUpdater
        //{
        //    internal void SetValues()
        //    {
        //        var ORef = new Function(); // Create an object reference for the Function class
        //        string curVer = ORef.GetProductVersion(); // Get the current version of your application

        //        // Next get the available version of your application by downloading a text file with your version numbers.
        //        string availVer = ORef.GetAvailVersion(Variables.txtFileVersionUrl);
        //        var oRefUI = new UpdateUI(); // Create an object reference for the UpdateUI class.

        //        // Next we create a thread, pass it an anonymous delegate method to pass in the object reference to the UpdateUI UPDATE method, along with the required parameters
        //        //Thread thread = new Thread(delegate () { oRefUI.UPDATE(oRefUI.CurrentVersion, oRefUI.AvailableVersion, true, "Checking for new versions...."); });
        //        //thread.Start();
        //    }
        //}

        public class Function
        {
            internal string GetProductVersion()
            {
                var oRef = new UpdateUI(); // Create an object refernce to the variables class so we can get the name of the file
                
                var fPath = Directory.GetCurrentDirectory(); // Be sure to build you application in the same directory as your updater or you will get an exception.
                var pathCombine = Path.Combine(fPath, Variables.AppFileName); // Combine the path and the file name to make a complete path including the file name

                if (File.Exists(pathCombine))
                {
                    var v = Assembly.LoadFile(pathCombine).GetName().Version.ToString(); // Load the path of the file, and acquire the location from pathcombined, and convert the version to string format.

                    Console.WriteLine(fPath);
                    Console.WriteLine(pathCombine);
                    Console.WriteLine(v);

                    //oRef.CurrentVersion = v;

                    return v;
                }
                else
                {
                    return "Unable to get current application version.";
                }
            }

            internal string GetAvailVersion(string onlineTxtFile)
            {
                var wClient = new WebClient(); // Create an instance of the WebClient.

                // Download the string of the URL. This should be a direct link to your text file and not a html file.
                string myResponse = "";
                if (!string.IsNullOrWhiteSpace(onlineTxtFile))
                {
                    try
                    {
                        myResponse = wClient.DownloadString(onlineTxtFile);

                        if (myResponse.Length > 8654)
                        {
                            string TestString = "Testing New Version: 2.0.0.0";
                            var subStr = myResponse.Substring(myResponse.IndexOf("Testing"));
                            var cleanedResponse = subStr.Remove(TestString.Length);
                            myResponse = cleanedResponse;
                        }
                    }
                    catch (WebException)
                    {
                        myResponse = "Unable to connect to update server.";
                    }
                }

                //Mainwindow.MW.pgBar.Value = 5; //This sets the progress bar through our sneaky static MW declartation in our main partial class.

                //// This should only be required if the link is to a html file and not a txt file
                ////The DownloadString method downloads the URL contents, and so given I was working with a URL and not a text file with only version numbers, we need to clean up...
                //var subStr = myResponse.Substring(8654); //There are 8654 chars in the source code up until the version numbers are reached, and we need to remove them using substring.
                //var cleanedResponse = subStr.Remove(7); //We then remove the last remaining chars from the substring leaving only the version numbers. Version numbers consist of 7 digits.

                return myResponse;
            }

            public bool DeleteFile(string fName)
            {
                if (File.Exists(fName)) // Check if the file of your main app exists, and if true, we delete it and download the new one.
                {
                    try
                    {
                        File.SetAttributes(fName, FileAttributes.Normal); // Set default attributes first.
                        File.Delete(fName); // Delete the file
                        return true;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("File not deleted, error is :: " + ex.StackTrace);
                        return false;
                    }
                }

                return false;
            }
        }

        public class UpdateUI
        {
            // Because this method was started from another thread, we dont own it, and so we can't access controls from here without delegating and invoking.
            public void UPDATE(string CurrentVersion, string AvailableVersion, bool seeProgress, string Status)
            {
                Application.Current.Dispatcher.Invoke(delegate // Use this delegate to put yourself back on talking terms with your UI by invoking the controls to set them.
                {
                    // This uses the current version we passed in from UpdateUI.CurrentVersion in the thread we started and passed the parameters. Otherwise, if i wanted to use the below, id use this.CurrentVersion
                    //Mainwindow.MW.AppVersionNum_Label.Content = CurrentVersion; //See ^^/>/>
                    //Mainwindow.MW.AvailAppVersionNum_Label.Content = AvailableVersion; //Same as above ^^/>/>
                    //Mainwindow.MW.StatusLabel.Content = Status; //Gives the status label a status through our static friend in the partial main method.
                    //Something to point out, we are in a new thread and operating off the UI, and so if you move the above three lines outside the dispatcher, you will get thread ownership exception. (Paraphraising)
                    if (CurrentVersion != AvailableVersion)
                    {
                        if (seeProgress == true)
                        {
                            //Mainwindow.MW.pgBar.Visibility = Visibility.Visible;
                            //MainViewModel viewModel = new MainViewModel();
                            //viewModel.ProgressMessage = "Testing new message.";
                            ProgressMessage = "Testing new message.";
                        } //Since we are updating the UI, as there are new versions, we will show the progress bar.

                        //You could also do some fancy timer stuff per tick and set the opacity of the pgBar to 0 and increase it for a fade in effect. ;)/>/>
                        //Console.WriteLine("Download new version to application directory.");
                        //Mainwindow.MW.StatusLabel.Content = "A new version is available. Downloading version " + AvailableVersion + ", please wait..."; //Just updating the status label to keep user informed.
                        //You should set some bool or a step process from this point to move to the next update method.
                        //You will from here finish off the update process yourself by following some todo tips after the code box...
                    }
                });
            }

            //public string CurrentVersion
            //{
            //    get
            //    {
            //        return Variables.CurrentVersion;
            //    }
            //    set
            //    {
            //        Variables.CurrentVersion = value;
            //    }
            //}

            //public string AvailableVersion
            //{
            //    get
            //    {
            //        return Variables.AvailableVersion;
            //    }
            //    set
            //    {
            //        Variables.AvailableVersion = value;
            //    }
            //}
        }
    }



The bit i am having trouble is below (which is inside the UpdateUI class):
// Because this method was started from another thread, we dont own it, and so we can't access controls from here without delegating and invoking.
            public void UPDATE(string CurrentVersion, string AvailableVersion, bool seeProgress, string Status)
            {
                Application.Current.Dispatcher.Invoke(delegate // Use this delegate to put yourself back on talking terms with your UI by invoking the controls to set them.
                {
                    // This uses the current version we passed in from UpdateUI.CurrentVersion in the thread we started and passed the parameters. Otherwise, if i wanted to use the below, id use this.CurrentVersion
                    //Mainwindow.MW.AppVersionNum_Label.Content = CurrentVersion; //See ^^/>/>
                    //Mainwindow.MW.AvailAppVersionNum_Label.Content = AvailableVersion; //Same as above ^^/>/>
                    //Mainwindow.MW.StatusLabel.Content = Status; //Gives the status label a status through our static friend in the partial main method.
                    //Something to point out, we are in a new thread and operating off the UI, and so if you move the above three lines outside the dispatcher, you will get thread ownership exception. (Paraphraising)
                    if (CurrentVersion != AvailableVersion)
                    {
                        if (seeProgress == true)
                        {
                            //Mainwindow.MW.pgBar.Visibility = Visibility.Visible;
                            //MainViewModel viewModel = new MainViewModel();
                            //viewModel.ProgressMessage = "Testing new message.";
                            ProgressMessage = "Testing new message.";
                        } //Since we are updating the UI, as there are new versions, we will show the progress bar.

                        //You could also do some fancy timer stuff per tick and set the opacity of the pgBar to 0 and increase it for a fade in effect. ;)/>/>
                        //Console.WriteLine("Download new version to application directory.");
                        //Mainwindow.MW.StatusLabel.Content = "A new version is available. Downloading version " + AvailableVersion + ", please wait..."; //Just updating the status label to keep user informed.
                        //You should set some bool or a step process from this point to move to the next update method.
                        //You will from here finish off the update process yourself by following some todo tips after the code box...
                    }
                });
            }



Looking at the below specifically you can see that i have tried what you suggested about adding an object reference:
//MainViewModel viewModel = new MainViewModel();
                            //viewModel.ProgressMessage = "Testing new message.";
                            ProgressMessage = "Testing new message.";



But by adding an object reference i am changing the value of the variable on the new object instead of on the one that is hooked up to bind to my view so my view won't get updated. Or maybe i'm misunderstanding something.

For the above non-commented out line i am getting the error: An object reference is required for the non-static field, method, or property 'MainViewModel.ProgressMessage'.
Was This Post Helpful? 0
  • +
  • -

#24 Sheepings   User is offline

  • Senior Programmer
  • member icon

Reputation: 203
  • View blog
  • Posts: 1,129
  • Joined: 05-December 13

Re: Application with built-in database handle updates without losing data

Posted 06 March 2019 - 07:37 AM

You either make the progressmessage static and or update it with a wrapper in your MainViewModel. Since progressmessage belongs to your MainViewModel, it is protected by MVVM. And since you're trying to access this property from another class, you will either need an object ref or sneak in a static declaration to allow you access to the variable(s) under the hood. There are a few ways to approach this, but I don't have much time to go into great detail as I'm currently at work. Maybe this example will help, or someone else will delve in to assist.

using System;
using System.Windows;

namespace WPFTestApp
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public int i = 5;
        public static Window1 MV; //this

        public Window1()
        {
            InitializeComponent();
            MV = this; //this
            var objRef_UC = new updaterClass();
            objRef_UC.updateUI("New Title Set");
            var objRef_Ext = new extClass();
            objRef_Ext.Call();
        }
    }
    public class extClass
    {
        public void Call()
        {
            var objRef_UC = new updaterClass();
            objRef_UC.updateUI("New Title Set from extClass");
            Console.WriteLine("Label1 is now titled :: " + Window1.MV.Label1.Content);
        }
    }
    public class updaterClass
    {
        public void updateUI(string nmw)
        {
            Console.WriteLine("Label1 was titled :: " + Window1.MV.Label1.Content);
            Window1.MV.Label1.Content = nmw;
            Console.WriteLine("Label1 is now titled :: " + Window1.MV.Label1.Content);
            Console.WriteLine("The value of [i] is :: " + Window1.MV.i);
            Window1.MV.i = 10;
            Console.WriteLine("The value of [i] is now :: " + Window1.MV.i);
        }
    }
}


OUTPUTS ::
Label1 was titled :: Label
Label1 is now titled :: New Title Set
The value of [i] is :: 5
The value of [i] is now :: 10
Label1 was titled :: New Title Set
Label1 is now titled :: New Title Set from extClass
The value of [i] is :: 10
The value of [i] is now :: 10
Label1 is now titled :: New Title Set from extClass

The other is to raise a static event which has access to your UI thread and set it to fire when a variable change is detected, and update. By the looks of it at glance, you removed the threading, so nothing to comment on about that. Please remove the comments from your code. I'm not reading all that....nor do i have time too. Only leave comments to reference an issue.

This post has been edited by Sheepings: 06 March 2019 - 02:25 PM

Was This Post Helpful? 0
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2