What's new
  • Visit Rebornbuddy
  • Visit Panda Profiles
  • Visit LLamamMagic
  • Visit Resources
  • Visit Downloads
  • Visit Portal
RebornBuddy Forums

Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

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

satbuster

Member
Joined
Dec 19, 2014
Messages
93
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-
 
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. :)
 
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.

WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
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.
 
Current relative files deployed in my test:
Agility/
Agility/Agility.cs
Agility/Settings/
Agility/Settings/AgilitySettings.g.cs
Agility/Settings/AgilitySettings.xaml

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:
...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();
            }
        }
 
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.
 
Thanks. Looking at persisting settings now and binding them to GUI controls.

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
 
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.
 
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();
        }
 
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?
 
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?

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;
        }
 
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.
 
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.

If you check your error stack, it determines the last bot by it's Equals method. =P

IE: You were throwing the exception yourself. ;)
 
Back
Top