• Visit Rebornbuddy
  • Example of "How to Use Code-Behind/XAML for your WPF GUIs"

    Discussion in 'Wildbuddy Developers' started by satbuster, Jul 11, 2015.

    1. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
      Hi,

      does anyone have a simple example of a WPF-based configuration control that is displayed when button is pressed from OnButtonClicked?

      Code:
      public interface IUIButtonProvider {
              string ButtonText { get; }
              void OnButtonClicked(object sender);
      }
      I have followed the sticky topic "How to Use Code-Behind/XAML for your WPF GUIs" and looked at the ExileBuddy posts and ExampleBot.

      I think I'm mostly struggling with the deployment to the Bots folder and getting the GUI components to load correctly.

      Code:
      System.Exception: The component 'CombatBotEx.CombatBotExGui' does not have a resource identified by the URI '/CombatBotEx;component/combatbotexgui.xaml'.
         at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
         at CombatBotEx.CombatBotExGui.InitializeComponent() in c:\Games\World of Wild\CombatBotExGui.xaml:line 1
         at CombatBotEx.CombatBotExGui..ctor() in c:\Games\World of Wild\Bots\CombatBotEx\CombatBotExGui.xaml.cs:line 22
         at CombatBotEx.CombatBotEx.get_Control() in c:\Games\World of Wild\Bots\CombatBotEx\CombatBotEx.cs:line 154
         at CombatBotEx.CombatBotEx.OnButtonClicked(Object sender) in c:\Games\World of Wild\Bots\CombatBotEx\CombatBotEx.cs:line 160
      I haven't seen a lot of WPF-based configuration dialog for Wildbuddy Bots. So not a lot of code to look at.


      -SB-
       
    2. Deathdisguise

      Deathdisguise Community Developer

      Joined:
      Mar 7, 2015
      Messages:
      678
      Likes Received:
      6
      Trophy Points:
      0
      I've been meaning to do config settings for a while now, so I'll slap something together and see what I can share! (I don't have much experience with GUIs, so it may need tinkering)

      Agility.cs (IBot class)
      Code:
      public string ButtonText
      {
          get { return "AGILITY"; }
      }
      
      public void OnButtonClicked(object sender)
      {
          MetroNavigationWindow settings = new MetroNavigationWindow()
          {
              Title = "Agility Settings",
              Content = new AgilitySettings()
          };
      
          settings.ShowDialog();
      }
      
      xaml file
      Code:
      <UserControl x:Class="Agility.Settings.AgilitySettings"
                   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" 
                   d:DesignHeight="300" d:DesignWidth="300">
          <Grid>
          </Grid>
      </UserControl>
      
      xaml.cs file
      Code:
      using System.Windows.Controls;
      
      namespace Agility.Settings
      {
          public partial class AgilitySettings : UserControl
          {
              public AgilitySettings()
              {
                  InitializeComponent();
              }
          }
      }
      
      

      For now this just shows a blank window/control, but from there it should be easy enough to add and format it how you want via the .xaml.

      As for your error, make sure you're copying your build files over to the working directory for testing. (Personally I don't like WPF, but I understand it's benefits)

      PS: I'm going to look for a way to get the slide out options pane to appear, I think that would look much more spiffy. :)
       
    3. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
      Thanks.

      this was useful. It wasn't clear to me how to embed the defined WPF content.
      Code:
      MetroNavigationWindow settings = new MetroNavigationWindow()
          {
              Title = "Agility Settings",
              Content = new AgilitySettings()
          };
      
          settings.ShowDialog();

      I'm still struggling with the deployment and runtime within Wildbuddy.

      To get InitializeComponent() to work, I need to deploy the generated .g.cs file, I think.
      Code:
      error CS0103: The name 'InitializeComponent' does not exist in the current context
      Right now, I deploy the following files.

      Code:
       Directory of C:\Games\World of Wild\Bots\CombatBotEx
      
      07/12/2015  07:50 AM    <DIR>          .
      07/12/2015  07:50 AM    <DIR>          ..
      07/12/2015  07:41 AM             6,192 CombatBotEx.cs
      07/12/2015  07:50 AM               780 CombatBotExGui.baml 
      07/12/2015  07:50 AM             3,033 CombatBotExGui.g.cs 
      07/11/2015  09:29 PM               613 CombatBotExGui.xaml
      07/12/2015  07:44 AM               303 CombatBotExGui.xaml.cs
      
      But I still have runtime issues. I've enabled logging using...

      Code:
      Key Name:          HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Fusion
      Value 0
        Name:            EnableLog
        Type:            REG_DWORD
        Data:            0x1
      and it shows a few runtime challenges.

      Code:
      LOG: This bind starts in default load context.
      LOG: Using application configuration file: C:\Games\World of Wild\Wildbuddy.exe.Config
      LOG: Using host configuration file: 
      LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
      LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
      LOG: Attempting download of new URL file:///C:/Games/World of Wild/CombatBotEx.DLL.
      LOG: Attempting download of new URL file:///C:/Games/World of Wild/CombatBotEx/CombatBotEx.DLL.
      LOG: Attempting download of new URL file:///C:/Games/World of Wild/CombatBotEx.EXE.
      LOG: Attempting download of new URL file:///C:/Games/World of Wild/CombatBotEx/CombatBotEx.EXE.
       
    4. Deathdisguise

      Deathdisguise Community Developer

      Joined:
      Mar 7, 2015
      Messages:
      678
      Likes Received:
      6
      Trophy Points:
      0
      Current relative files deployed in my test:
      I'm fairly sure the rest of the files are superfluous and just break things anyways. :)


      EDIT: Or.... hahaha, I just realized why the above strategy isn't going to work, when I attempted to put a control in the new window. Back to the drawing board! ;)
       
      Last edited: Jul 11, 2015
    5. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
    6. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
      ...old way seems to work. Managed to pull up a window with a button inside.

      Code:
       public void OnButtonClicked(object sender) {
                  
                  Log.Info("Opening Configuration");
      
                  using (var fs = new FileStream(@"Bots\CombatBotEx\CombatBotExGui.xaml", FileMode.Open)) {
                      var root = (UserControl)XamlReader.Load(fs);
                      MetroNavigationWindow configWindow = new MetroNavigationWindow() {
                          Title = "CombatBotEx Settings",
                          Content = root
                      };
                      configWindow.ShowDialog();
                  }
              } 
       
    7. Deathdisguise

      Deathdisguise Community Developer

      Joined:
      Mar 7, 2015
      Messages:
      678
      Likes Received:
      6
      Trophy Points:
      0
      Ah, so loading it directly from the xaml! Good 'nuff for me! :p
       
    8. Deathdisguise

      Deathdisguise Community Developer

      Joined:
      Mar 7, 2015
      Messages:
      678
      Likes Received:
      6
      Trophy Points:
      0
      Just wanted to share my completion of a Settings Flyout with you and anyone interested! There's also a working example in Agility if you'd like to see it.

      BotBase.cs
      Code:
      public string ButtonText
              {
                  get { return "AGILITY"; }
              }
      
              private Flyout AgilitySettings = null;
              public void OnButtonClicked(object sender)
              {
                  MetroWindow MainWindow = (MetroWindow)Buddy.Wildstar.App.Current.MainWindow;
      
                  // Check to see if AgilitySettings is already open, if so, simply toggle the visibility.
                  if (AgilitySettings != null || MainWindow.Flyouts.Items.Contains(AgilitySettings))
                      AgilitySettings.IsOpen = !AgilitySettings.IsOpen;
      
                  // Not open? Lets make a new one and add it to the MetroWindow.
                  else
                  {
                      using (var fs = new FileStream(@"Bots\Agility\Settings\AgilitySettings.xaml", FileMode.Open))
                      {
                          AgilitySettings = (Flyout)XamlReader.Load(fs);
                          MainWindow.Flyouts.Items.Add(AgilitySettings);
                      }
                  }
              }
      
      Settings.xaml
      Code:
      <Metro:Flyout
          xmlns:Metro="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
          AnimateOpacity="True"
          IsOpen="True"
          AnimateOnPositionChange="True"
          Name="AgilitySettings"
          Header="Agility Settings"
          Width="365">
      </Metro:Flyout>
      
      Only the .xaml file is needed in this implementation, which makes it super easy for setting up a solution.
       
    9. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
      Thanks. Looking at persisting settings now and binding them to GUI controls.
       
    10. Deathdisguise

      Deathdisguise Community Developer

      Joined:
      Mar 7, 2015
      Messages:
      678
      Likes Received:
      6
      Trophy Points:
      0
      I feel like since we are manually loading the xaml file, we're defeating the purpose of the WPF, but at the same time I can't figure out how to beat the InitializeComponent() method error. That being said, currently getting smashed with a rapid learning curve on setting two-way bindings as well. =P
       
    11. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
      I had some success with Wildbuddy settings, WPF checkboxes and two-way binding (I think).


      Example of my configuration class and how it persists the config in the right wildbuddy directory using GetSettingsFilePath()...
      Code:
      namespace CombatBotEx {
      
          class CombatBotExConfig : JsonSettings {
      
              private ILog Log = LogManager.GetLogger("CombatBotExConfig");
      
              private static CombatBotExConfig _instance;
      
              public static CombatBotExConfig Instance {
                  get { return _instance ?? (_instance = new CombatBotExConfig()); }
              }
      
              public CombatBotExConfig()
                  : base(GetSettingsFilePath(SettingsPath, "CombatBotEx", Buddy.Wildstar.Game.GameManager.LocalPlayer.Name, string.Format("{0}.json", "CombatBotExConfig"))) {
              }

      Example of a configuration property in the above configuration class...
      Code:
              #region SettingsAutoCollect Binding
      
              private bool _SettingsAutoCollect;
              [DefaultValue(true)]
              public bool SettingsAutoCollect {
                  get { return _SettingsAutoCollect; }
                  set {
                      if (value.Equals(_SettingsAutoCollect)) {
                          return;
                      }
                      Log.Info("SettingsAutoCollect(" + value + ")");
                      _SettingsAutoCollect = value;
                      NotifyPropertyChanged(() => SettingsAutoCollect);
                  }
              }
      
              #endregion

      Example of the two-way binding defined in the XAML...
      Code:
      <CheckBox x:Name="AutoCollectCheckBox" Content="Auto-Collect" Padding="3" IsChecked="{Binding SettingsAutoCollect, Mode=TwoWay, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"/>

      Example of the linking (DataContext) between the XAML GUI and the configuration class...
      Code:
      using (var fs = new FileStream(@"Bots\CombatBotEx\CombatBotExGui.xaml", FileMode.Open)) {
                          var root = (UserControl)XamlReader.Load(fs);
                          root.DataContext = CombatBotExConfig.Instance; // WPF DataBinding, bind it to our CombatBotExConfig instance
                          return root;
                      };


      Seems to work.
       
    12. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
    13. Deathdisguise

      Deathdisguise Community Developer

      Joined:
      Mar 7, 2015
      Messages:
      678
      Likes Received:
      6
      Trophy Points:
      0
    14. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
      what does base.Save() do? Config seems to get persisted when Wildbuddy exits.
       
    15. Deathdisguise

      Deathdisguise Community Developer

      Joined:
      Mar 7, 2015
      Messages:
      678
      Likes Received:
      6
      Trophy Points:
      0
      It pushes the change to the config file. This way, if WB crashes, it'll retain the settings. :)
       
    16. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
      Seems easy enough. Too late to test it tonight.

      Code:
      public void OnButtonClicked(object sender) {
                  Log.Info("Opening Configuration Dialog");
                  MetroNavigationWindow configWindow = new MetroNavigationWindow() {
                      Title = "CombatBotEx Settings",
                      Content = this.Control,
                      ShowCloseButton = false,
                      ShowMinButton = false,
                      ShowMaxRestoreButton = false,
                      ShowIconOnTitleBar = false,
                      SizeToContent = System.Windows.SizeToContent.WidthAndHeight
                  };
                  configWindow.ShowDialog();
                  CombatBotExConfig.Instance.Save();
              }
       
    17. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
      Another quality-of-life question. Everytime I start wildbuddy, it seems to default to CombatBot (which co-incidentally then throws a not yet implemented exception). Do you know how I can persist that my bot (CombatBotEx) was the last Bot across wildbuddy restarts?
       
    18. Deathdisguise

      Deathdisguise Community Developer

      Joined:
      Mar 7, 2015
      Messages:
      678
      Likes Received:
      6
      Trophy Points:
      0
      Implement IBot.Equals()

      Code:
      public bool Equals(IBot other)
              {
                  if (this.Name != other.Name)
                      return false;
      
                  if (this.Version != other.Version)
                      return false;
      
                  if (this.Author != other.Author)
                      return false;
      
                  return true;
              }
      
       
    19. satbuster

      satbuster Member

      Joined:
      Dec 19, 2014
      Messages:
      93
      Likes Received:
      0
      Trophy Points:
      6
      Thanks. That worked. A bit of a shame that a bot with a problem prevents WildBuddy from switching to the last bot. Would be nice if they caught exceptions thrown by bot and continued.
       
    20. Deathdisguise

      Deathdisguise Community Developer

      Joined:
      Mar 7, 2015
      Messages:
      678
      Likes Received:
      6
      Trophy Points:
      0
      If you check your error stack, it determines the last bot by it's Equals method. =P

      IE: You were throwing the exception yourself. ;)
       

    Share This Page