diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/App.config b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/App.config new file mode 100644 index 00000000..d0feca6f --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/App.xaml b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/App.xaml new file mode 100644 index 00000000..ad033380 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/App.xaml @@ -0,0 +1,7 @@ + + + + + diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/App.xaml.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/App.xaml.cs new file mode 100644 index 00000000..4594a485 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/App.xaml.cs @@ -0,0 +1,17 @@ +using System.Windows; +using LidgrenWpfChat.Client.View; +using LidgrenWpfChat.Client.ViewModel; + +namespace LidgrenWpfChat.Client +{ + public partial class App : Application + { + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + + MainView view = new MainView() { DataContext = new MainViewModel() }; + view.Show(); + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/LidgrenWpfChat.Client.csproj b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/LidgrenWpfChat.Client.csproj new file mode 100644 index 00000000..1d3c6c91 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/LidgrenWpfChat.Client.csproj @@ -0,0 +1,114 @@ + + + + + Debug + AnyCPU + {2F201A9A-3C00-41D1-8C49-45632F663088} + WinExe + Properties + LidgrenWpfChat.Client + LidgrenWpfChat.Client + v4.5.1 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\Lidgren.Network\bin\Debug\Lidgren.Network.dll + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + MainView.xaml + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + {feece238-25c2-490e-81df-b3a36a07a284} + LidgrenWpfChat.Common + + + + + Designer + MSBuild:Compile + + + + + + \ No newline at end of file diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/MessageConstants.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/MessageConstants.cs new file mode 100644 index 00000000..826b90fa --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/MessageConstants.cs @@ -0,0 +1,15 @@ +namespace LidgrenWpfChat.Client +{ + public class MessageConstants + { + public static string ErrorTitle = "Error"; + public static string NoUrlText = "You need to provide a URL to connect to."; + public static string NoPortText = "You need to provide a Port to connect to."; + public static string PortFailConvert = "The text you provided did not convert to a proper port number"; + public static string NoLidgrenIdText = "You need to provide a LidgrenId to connect with."; + public static string NoUserNameText = "You need to provide a UserName to connect with."; + + public static string Connect = "Connect"; + public static string Disconnect = "Disconnect"; + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/AssemblyInfo.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..9e9fc2ce --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LidgrenWpfChat.Client")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LidgrenWpfChat.Client")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Resources.Designer.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Resources.Designer.cs new file mode 100644 index 00000000..f7151cf9 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34014 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LidgrenWpfChat.Client.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LidgrenWpfChat.Client.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Resources.resx b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Resources.resx new file mode 100644 index 00000000..af7dbebb --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Settings.Designer.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Settings.Designer.cs new file mode 100644 index 00000000..1a5d88ea --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34014 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LidgrenWpfChat.Client.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Settings.settings b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Settings.settings new file mode 100644 index 00000000..033d7a5e --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/View/Behavior/ListBoxBehavior.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/View/Behavior/ListBoxBehavior.cs new file mode 100644 index 00000000..1e8a43f7 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/View/Behavior/ListBoxBehavior.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Windows; +using System.Windows.Controls; + +namespace LidgrenWpfChat.Client.View.Behavior +{ + /* + * + * http://stackoverflow.com/a/2007072/653076 + * + */ + public class ListBoxBehavior + { + static Dictionary Associations = + new Dictionary(); + + public static bool GetScrollOnNewItem(DependencyObject obj) + { + return (bool)obj.GetValue(ScrollOnNewItemProperty); + } + + public static void SetScrollOnNewItem(DependencyObject obj, bool value) + { + obj.SetValue(ScrollOnNewItemProperty, value); + } + + public static readonly DependencyProperty ScrollOnNewItemProperty = + DependencyProperty.RegisterAttached( + "ScrollOnNewItem", + typeof(bool), + typeof(ListBoxBehavior), + new UIPropertyMetadata(false, OnScrollOnNewItemChanged)); + + public static void OnScrollOnNewItemChanged( + DependencyObject d, + DependencyPropertyChangedEventArgs e) + { + var listBox = d as ListBox; + if (listBox == null) return; + bool oldValue = (bool)e.OldValue, newValue = (bool)e.NewValue; + if (newValue == oldValue) return; + if (newValue) + { + listBox.Loaded += new RoutedEventHandler(ListBox_Loaded); + listBox.Unloaded += new RoutedEventHandler(ListBox_Unloaded); + } + else + { + listBox.Loaded -= ListBox_Loaded; + listBox.Unloaded -= ListBox_Unloaded; + if (Associations.ContainsKey(listBox)) + Associations[listBox].Dispose(); + } + } + + static void ListBox_Unloaded(object sender, RoutedEventArgs e) + { + var listBox = (ListBox)sender; + if (Associations.ContainsKey(listBox)) + Associations[listBox].Dispose(); + listBox.Unloaded -= ListBox_Unloaded; + } + + static void ListBox_Loaded(object sender, RoutedEventArgs e) + { + var listBox = (ListBox)sender; + var incc = listBox.Items as INotifyCollectionChanged; + if (incc == null) return; + listBox.Loaded -= ListBox_Loaded; + Associations[listBox] = new Capture(listBox); + } + + class Capture : IDisposable + { + public ListBox listBox { get; set; } + public INotifyCollectionChanged incc { get; set; } + + public Capture(ListBox listBox) + { + this.listBox = listBox; + incc = listBox.ItemsSource as INotifyCollectionChanged; + if (incc != null) + { + incc.CollectionChanged += + new NotifyCollectionChangedEventHandler(incc_CollectionChanged); + } + } + + void incc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Add) + { + listBox.ScrollIntoView(e.NewItems[0]); + listBox.SelectedItem = e.NewItems[0]; + } + } + + public void Dispose() + { + if (incc != null) + incc.CollectionChanged -= incc_CollectionChanged; + } + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/View/MainView.xaml b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/View/MainView.xaml new file mode 100644 index 00000000..60a22abc --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/View/MainView.xaml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/View/MainView.xaml.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/View/MainView.xaml.cs new file mode 100644 index 00000000..8f047e4f --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/View/MainView.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace LidgrenWpfChat.Client.View +{ + public partial class MainView : Window + { + public MainView() + { + InitializeComponent(); + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/ViewModel/MainViewModel.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/ViewModel/MainViewModel.cs new file mode 100644 index 00000000..74f1c842 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/ViewModel/MainViewModel.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using System.Windows.Threading; +using Lidgren.Network; +using LidgrenWpfChat.Common; + +namespace LidgrenWpfChat.Client.ViewModel +{ + public enum CurrentStatus + { + Connected, + Disconnected + } + + public class MainViewModel : ViewModelBase + { + private CurrentStatus currentStatus; + + private string urlText; + private string portText; + private string lidgrenIdText; + private string userNameText; + private string connectButtonText; + private string userMessage; + + private ICommand connectButtonCommand; + private ICommand sendCommand; + + private ObservableCollection chatMessages; + + private NetClient client; + + public MainViewModel() + { + ChatMessages = new ObservableCollection(); + UrlText = String.Empty; + PortText = String.Empty; + LidgrenIdText = String.Empty; + ConnectButtonText = MessageConstants.Connect; + currentStatus = CurrentStatus.Disconnected; + + Application.Current.Exit += ApplicationShuttingDown; + } + + private void HandleConnectButtonClick() + { + switch (currentStatus) + { + case CurrentStatus.Connected: + Disconnect(); + break; + case CurrentStatus.Disconnected: + Connect(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private bool ValidateInput() + { + if (String.IsNullOrWhiteSpace(UrlText)) + { + MessageBox.Show(MessageConstants.NoUrlText, MessageConstants.ErrorTitle, MessageBoxButton.OK, + MessageBoxImage.Error); + return false; + } + + if (String.IsNullOrWhiteSpace(PortText)) + { + MessageBox.Show(MessageConstants.NoPortText, MessageConstants.ErrorTitle, MessageBoxButton.OK, + MessageBoxImage.Error); + return false; + } + + int portNumber; + + if (!Int32.TryParse(PortText, out portNumber)) + { + MessageBox.Show(MessageConstants.PortFailConvert, MessageConstants.ErrorTitle, MessageBoxButton.OK, + MessageBoxImage.Error); + return false; + } + + if (String.IsNullOrWhiteSpace(LidgrenIdText)) + { + MessageBox.Show(MessageConstants.NoLidgrenIdText, MessageConstants.ErrorTitle, MessageBoxButton.OK, + MessageBoxImage.Error); + return false; + } + + return true; + } + + private void Connect() + { + if (!ValidateInput()) + { + return; + } + + NetPeerConfiguration config = new NetPeerConfiguration(LidgrenIdText); + config.AutoFlushSendQueue = true; + + client = new NetClient(config); + + client.RegisterReceivedCallback(GotMessage); + + client.Start(); + + NetOutgoingMessage hail = client.CreateMessage("Hail from: "+client.UniqueIdentifier); + + int port; + Int32.TryParse(PortText, out port); + client.Connect(UrlText, port, hail); + } + + private void ShowMessage(string message) + { + ChatMessages.Add(DateTime.Now + ": " + message); + } + + private void GotMessage(object state) + { + Task task = Task.Run(() => + { + NetIncomingMessage incomingMessage; + + while ((incomingMessage = client.ReadMessage()) != null) + { + switch (incomingMessage.MessageType) + { + case NetIncomingMessageType.DebugMessage: + case NetIncomingMessageType.WarningMessage: + case NetIncomingMessageType.ErrorMessage: + case NetIncomingMessageType.VerboseDebugMessage: + string text = incomingMessage.ReadString(); + Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, + new Action(() => ShowMessage(text))); + break; + case NetIncomingMessageType.StatusChanged: + NetConnectionStatus status = (NetConnectionStatus)incomingMessage.ReadByte(); + + if (status == NetConnectionStatus.Connected) + { + ConnectButtonText = MessageConstants.Disconnect; + currentStatus = CurrentStatus.Connected; + } + else + { + ConnectButtonText = MessageConstants.Connect; + currentStatus = CurrentStatus.Disconnected; + } + + string reason = incomingMessage.ReadString(); + + Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, + new Action(() => ShowMessage(reason))); + + break; + case NetIncomingMessageType.Error: + case NetIncomingMessageType.UnconnectedData: + case NetIncomingMessageType.ConnectionApproval: + case NetIncomingMessageType.Data: + string chat = incomingMessage.ReadString(); + + Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, + new Action(() => ShowMessage(chat))); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + client.Recycle(incomingMessage); + } + }); + } + + private void SendMessage() + { + if (currentStatus != CurrentStatus.Connected) + { + return; + } + + NetOutgoingMessage outgoingMessage = client.CreateMessage(UserMessage); + client.SendMessage(outgoingMessage, NetDeliveryMethod.ReliableOrdered); + UserMessage = String.Empty; + client.FlushSendQueue(); + } + + private void Disconnect() + { + client.Disconnect("Requested by user"); + currentStatus = CurrentStatus.Disconnected; + ConnectButtonText = MessageConstants.Connect; + } + + private void ApplicationShuttingDown(object sender, ExitEventArgs e) + { + if (client == null) return; + + if (client.ConnectionStatus == NetConnectionStatus.Connected) + { + Disconnect(); + } + } + + public string UserMessage + { + get { return userMessage; } + set + { + userMessage = value; + OnPropertyChanged("UserMessage"); + } + } + + public string UrlText + { + get + { + return urlText; + } + set + { + urlText = value; + OnPropertyChanged("UrlText"); + } + } + + public string PortText + { + get { return portText; } + set + { + portText = value; + OnPropertyChanged("PortText"); + } + } + + public string LidgrenIdText + { + get { return lidgrenIdText; } + set + { + lidgrenIdText = value; + OnPropertyChanged("LidgrenIdText"); + } + } + + public string ConnectButtonText + { + get { return connectButtonText; } + set + { + connectButtonText = value; + OnPropertyChanged("ConnectButtonText"); + } + + } + + public ICommand ConnectButtonCommand + { + get { return connectButtonCommand ?? (connectButtonCommand = new RelayCommand(param => HandleConnectButtonClick())); } + } + + public ICommand SendCommand + { + get { return sendCommand ?? (sendCommand = new RelayCommand(param => SendMessage())); } + } + + public ObservableCollection ChatMessages + { + get { return chatMessages; } + set + { + chatMessages = value; + OnPropertyChanged("ChatMessages"); + } + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/ViewModel/ViewModelBase.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/ViewModel/ViewModelBase.cs new file mode 100644 index 00000000..424be87a --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Client/ViewModel/ViewModelBase.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace LidgrenWpfChat.Client.ViewModel +{ + public class ViewModelBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/Behavior/ListBoxBehavior.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/Behavior/ListBoxBehavior.cs new file mode 100644 index 00000000..c5a98319 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/Behavior/ListBoxBehavior.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Windows; +using System.Windows.Controls; + +namespace LidgrenWpfChat.Common.Behavior +{ + /* + * + * http://stackoverflow.com/a/2007072/653076 + * + */ + public class ListBoxBehavior + { + static Dictionary Associations = + new Dictionary(); + + public static bool GetScrollOnNewItem(DependencyObject obj) + { + return (bool)obj.GetValue(ScrollOnNewItemProperty); + } + + public static void SetScrollOnNewItem(DependencyObject obj, bool value) + { + obj.SetValue(ScrollOnNewItemProperty, value); + } + + public static readonly DependencyProperty ScrollOnNewItemProperty = + DependencyProperty.RegisterAttached( + "ScrollOnNewItem", + typeof(bool), + typeof(ListBoxBehavior), + new UIPropertyMetadata(false, OnScrollOnNewItemChanged)); + + public static void OnScrollOnNewItemChanged( + DependencyObject d, + DependencyPropertyChangedEventArgs e) + { + var listBox = d as ListBox; + if (listBox == null) return; + bool oldValue = (bool)e.OldValue, newValue = (bool)e.NewValue; + if (newValue == oldValue) return; + if (newValue) + { + listBox.Loaded += new RoutedEventHandler(ListBox_Loaded); + listBox.Unloaded += new RoutedEventHandler(ListBox_Unloaded); + } + else + { + listBox.Loaded -= ListBox_Loaded; + listBox.Unloaded -= ListBox_Unloaded; + if (Associations.ContainsKey(listBox)) + Associations[listBox].Dispose(); + } + } + + static void ListBox_Unloaded(object sender, RoutedEventArgs e) + { + var listBox = (ListBox)sender; + if (Associations.ContainsKey(listBox)) + Associations[listBox].Dispose(); + listBox.Unloaded -= ListBox_Unloaded; + } + + static void ListBox_Loaded(object sender, RoutedEventArgs e) + { + var listBox = (ListBox)sender; + var incc = listBox.Items as INotifyCollectionChanged; + if (incc == null) return; + listBox.Loaded -= ListBox_Loaded; + Associations[listBox] = new Capture(listBox); + } + + class Capture : IDisposable + { + public ListBox listBox { get; set; } + public INotifyCollectionChanged incc { get; set; } + + public Capture(ListBox listBox) + { + this.listBox = listBox; + incc = listBox.ItemsSource as INotifyCollectionChanged; + if (incc != null) + { + incc.CollectionChanged += + new NotifyCollectionChangedEventHandler(incc_CollectionChanged); + } + } + + void incc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Add) + { + listBox.ScrollIntoView(e.NewItems[0]); + listBox.SelectedItem = e.NewItems[0]; + } + } + + public void Dispose() + { + if (incc != null) + incc.CollectionChanged -= incc_CollectionChanged; + } + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/LidgrenWpfChat.Common.csproj b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/LidgrenWpfChat.Common.csproj new file mode 100644 index 00000000..68eb2f4d --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/LidgrenWpfChat.Common.csproj @@ -0,0 +1,55 @@ + + + + + Debug + AnyCPU + {FEECE238-25C2-490E-81DF-B3A36A07A284} + Library + Properties + LidgrenWpfChat.Common + LidgrenWpfChat.Common + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/MessageConstants.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/MessageConstants.cs new file mode 100644 index 00000000..94139441 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/MessageConstants.cs @@ -0,0 +1,18 @@ +namespace LidgrenWpfChat.Client +{ + public class MessageConstants + { + public static string ErrorTitle = "Error"; + public static string NoUrlText = "You need to provide a URL to connect to."; + public static string NoPortText = "You need to provide a Port to connect to."; + public static string PortFailConvert = "The text you provided did not convert to a proper port number"; + public static string NoLidgrenIdText = "You need to provide a LidgrenId to connect with."; + public static string NoUserNameText = "You need to provide a UserName to connect with."; + + public static string Connect = "Connect"; + public static string Disconnect = "Disconnect"; + + public static string Start = "Start"; + public static string Stop = "Stop"; + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/Properties/AssemblyInfo.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..81389e0e --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LidgrenWpfChat.Common")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LidgrenWpfChat.Common")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3cc83821-35be-492a-a5c3-26c80e4cf9ce")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/RelayCommand.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/RelayCommand.cs new file mode 100644 index 00000000..795645f2 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/RelayCommand.cs @@ -0,0 +1,43 @@ +using System; +using System.Diagnostics; +using System.Windows.Input; + +namespace LidgrenWpfChat.Common +{ + public class RelayCommand : ICommand + { + readonly Action execute; + readonly Predicate canExecute; + + public RelayCommand(Action execute) + : this(execute, null) + { + } + + public RelayCommand(Action execute, Predicate canExecute) + { + if (execute == null) + throw new ArgumentNullException("execute"); + + this.execute = execute; + this.canExecute = canExecute; + } + + [DebuggerStepThrough] + public bool CanExecute(object parameter) + { + return canExecute == null ? true : canExecute(parameter); + } + + public event EventHandler CanExecuteChanged + { + add { CommandManager.RequerySuggested += value; } + remove { CommandManager.RequerySuggested -= value; } + } + + public void Execute(object parameter) + { + execute(parameter); + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/ViewModelBase.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/ViewModelBase.cs new file mode 100644 index 00000000..424be87a --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Common/ViewModelBase.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace LidgrenWpfChat.Client.ViewModel +{ + public class ViewModelBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/App.config b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/App.config new file mode 100644 index 00000000..d0feca6f --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/App.xaml b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/App.xaml new file mode 100644 index 00000000..245c8a66 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/App.xaml @@ -0,0 +1,7 @@ + + + + + diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/App.xaml.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/App.xaml.cs new file mode 100644 index 00000000..83f120cf --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/App.xaml.cs @@ -0,0 +1,17 @@ +using System.Windows; +using LidgrenWpfChat.Server.View; +using LidgrenWpfChat.Server.ViewModel; + +namespace LidgrenWpfChat.Server +{ + public partial class App : Application + { + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + + MainView view = new MainView() {DataContext = new MainViewModel()}; + view.Show(); + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/LidgrenWpfChat.Server.csproj b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/LidgrenWpfChat.Server.csproj new file mode 100644 index 00000000..9b68489f --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/LidgrenWpfChat.Server.csproj @@ -0,0 +1,113 @@ + + + + + Debug + AnyCPU + {DF7686FD-9930-43E3-9A61-3E9A68CDA2A7} + WinExe + Properties + LidgrenWpfChat.Server + LidgrenWpfChat.Server + v4.5.1 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\Lidgren.Network\bin\Debug\Lidgren.Network.dll + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + MainView.xaml + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + {feece238-25c2-490e-81df-b3a36a07a284} + LidgrenWpfChat.Common + + + + + Designer + MSBuild:Compile + + + + + \ No newline at end of file diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/AssemblyInfo.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..18fcceb1 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LidgrenWpfChat.Server")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LidgrenWpfChat.Server")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Resources.Designer.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Resources.Designer.cs new file mode 100644 index 00000000..5138e826 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34014 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LidgrenWpfChat.Server.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LidgrenWpfChat.Server.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Resources.resx b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Resources.resx new file mode 100644 index 00000000..af7dbebb --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Settings.Designer.cs b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Settings.Designer.cs new file mode 100644 index 00000000..7f6a4344 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34014 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LidgrenWpfChat.Server.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Settings.settings b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Settings.settings new file mode 100644 index 00000000..033d7a5e --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/View/MainView.xaml b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/View/MainView.xaml new file mode 100644 index 00000000..691bb0c5 --- /dev/null +++ b/Samples/LidgrenWpfChat/LidgrenWpfChat.Server/View/MainView.xaml @@ -0,0 +1,18 @@ + + + + + + + + + + + +