When debuging the window doesn't appear - winforms

`using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace Server_chat
{
class server
{
private static int myProt = 10000;
private static byte[] buf2 = new byte[1024];
private static Socket serverSocket;
private static string message2;
/// <summary>
/// server socket
/// </summary>
public void Server_run()
{
IPHostEntry ipHost = Dns.Resolve(Dns.GetHostName());
IPAddress ip = ipHost.AddressList[0];
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(new IPEndPoint(ip,Program.form1.localport));
serverSocket.Listen(10);
Program.form1.myscan_box = "启动监听成功 !!";
Thread myThread = new Thread(ListenClientConnect);
myThread.IsBackground = true;
myThread.Start();
}
/// <summary>
/// 监听客户端连接
/// </summary>
private static void ListenClientConnect()
{
while (true) // 点对点通信
{
Socket clientSocket = serverSocket.Accept();
if (clientSocket.Connected == true)
{
IPEndPoint client_endpoint = (IPEndPoint)clientSocket.RemoteEndPoint;
Program.form1.myclientip_box = client_endpoint.Address.ToString() + "\\" + client_endpoint.Port.ToString();
Program.form1.myscan_box = "连接客户端 " + client_endpoint.Address.ToString() + " 成功 !!";
//服务端接收线程
Thread receiveThread = new Thread(ReceiveMessage);
receiveThread.IsBackground = true;
receiveThread.Start(clientSocket);
//服务端发送线程
Thread sendThread = new Thread(SendMessage);
sendThread.IsBackground = true;
sendThread.Start(clientSocket);
break;
}
}
}
/// <summary>
/// receive message
/// </summary>
/// <param name="clientSocket"></param>
private static void ReceiveMessage(object clientSocket)
{
Socket myClientSocket = (Socket)clientSocket;
while (true)
{
try
{
int len = myClientSocket.Receive(buf2);
message2 = Encoding.ASCII.GetString(buf2).Substring(0, len);
IPEndPoint client_endpoint = (IPEndPoint)myClientSocket.RemoteEndPoint;
Program.form1.myscan_box = client_endpoint.Address +": "+ message2;
}
catch (Exception ex)
{
Program.form1.myscan_box = ex.Message;
myClientSocket.Shutdown(SocketShutdown.Both);
myClientSocket.Close();
break;
}
}
}
/// <summary>
/// send message
/// </summary>
/// <param name="clientSocket"></param>
private static void SendMessage(object clientSocket)
{
Socket myClientSocket = (Socket)clientSocket;
while (true)
{
if (Program.form1.send_flag11 == 1)
{
myClientSocket.Send(Encoding.ASCII.GetBytes(Program.form1.message11));
Program.form1.send_flag11 = 0;
}
}
}
}
}
`When starting or debugging my program the window doesn't appear. But vs2012 suggests the debug session is ready.
When startting the program, the window appears but a popup window with the message "the program has stopped working, choose to debug or stop".
What's the problem? What should I do?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Threading;
namespace Server_chat
{
public partial class Form1 : Form
{
public string localip;
public int localport = 10002;
public string s_sendMessage;
public static int send_flag = 0;
public string message11
{
get { return s_sendMessage; }
}
public int send_flag11
{
get { return send_flag; }
set { send_flag = value; }
}
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;
IPHostEntry ipHost = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddr = ipHost.AddressList[0];
localip = ipAddr.ToString();
myip_box.Text = localip;
port_box.Text = localport.ToString();
server myserver = new server();
Thread th_server = new Thread(myserver.Server_run);
th_server.IsBackground = true;
th_server.Start();
}
private void setting_btn_Click(object sender, EventArgs e)
{
localport = Int16.Parse(port_box.Text);
scan_box.AppendText("\r\n" + "端口改成: " + port_box.Text);
}
private void send_btn_Click(object sender, EventArgs e)
{
s_sendMessage = chat_box.Text;
chat_box.Text = "";
scan_box.AppendText(s_sendMessage + "\r\n");
send_flag = 1;
}
}
}

Related

WPF ListView with large collection hangs GUI

++++++ Link to example project ++++++
I have a file that can contain thousands of lines of logged messages. I am parsing this file and adding each line (as a log event) to a collection. This collection should then be shown in a ListView.
As below:
<ListView
Grid.Row="0"
Margin="5"
ItemsSource="{Binding SelectedSerilogFileLog.LogEvents}"
ScrollViewer.CanContentScroll="False"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectedItem="{Binding SelectedLogEvent}"
SelectionMode="Single">
The parsing of the file (this one contains 2500+ log events) and adding to the collection takes around 100ms. Then when the bound collection is updated with the ReplaceContent method (this suppresses the collectionchanged event firing on every item added) the GUI hangs, but I cannot see why or what can be causing this.
MainWindow.cs
...
/// <summary>
///
/// </summary>
public SerilogFileLog SelectedSerilogFileLog
{
get => selectedSerilogFileLog; set
{
if (selectedSerilogFileLog != null)
{
SelectedSerilogFileLog.OnSerilogParserFinished -= OnSerilogParserFinished;
SelectedSerilogFileLog.OnSerilogParserProgressChanged -= OnSerilogParserProgressChanged;
}
selectedSerilogFileLog = value;
if (selectedSerilogFileLog != null)
{
ParserProgress = 0;
SelectedSerilogFileLog.OnSerilogParserFinished += OnSerilogParserFinished;
SelectedSerilogFileLog.OnSerilogParserProgressChanged += OnSerilogParserProgressChanged;
sw.Start();
SelectedSerilogFileLog.Parse();
}
NotifyPropertyChanged(nameof(SelectedSerilogFileLog));
}
}
...
private void Button_Click(object sender, RoutedEventArgs e)
{
SelectedSerilogFileLog = null;
SelectedSerilogFileLog = new SerilogFileLog() { FilePath = "Application20210216.log" };
}
The parsing and loading of the items occurs in a separate Task.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace LargeListViewTest.Classes
{
public class SerilogFileLog : INotifyPropertyChanged
{
private LogEvent lastLogEvent;
private ObservableCollectionEx<LogEvent> logEvents;
private string name;
private string description;
private string filePath;
private Regex patternMatching;
private string matchExpression = #"^(?<DateTime>[^|]+)\| (?<Level>[^|]+) \| (?<MachineName>[^|]+) \| (?<Source>[^|]+) \| (?<Message>[^$]*)$";
public delegate void SerilogParserProgressHandler(int Percentage);
public delegate void SerilogParserFinishedHandler();
/// <summary>
///
/// </summary>
public event SerilogParserProgressHandler OnSerilogParserProgressChanged;
/// <summary>
///
/// </summary>
public event SerilogParserFinishedHandler OnSerilogParserFinished;
/// <summary>
/// Gets or sets the LogEvents.
/// </summary>
public ObservableCollectionEx<LogEvent> LogEvents { get => logEvents; private set { logEvents = value; NotifyPropertyChanged(nameof(LogEvents)); } }
/// <summary>
/// Gets or sets the Name.
/// </summary>
public string Name { get => name; private set { name = value; NotifyPropertyChanged(nameof(Name)); } }
/// <summary>
/// Gets or sets the Description.
/// </summary>
public string Description { get => description; private set { description = value; NotifyPropertyChanged(nameof(Description)); } }
/// <summary>
/// Gets or sets the FilePath.
/// </summary>
public string FilePath
{
get => filePath;
set
{
filePath = value;
Name = Path.GetFileNameWithoutExtension(value);
Description = FilePath;
}
}
/// <summary>
///
/// </summary>
public SerilogFileLog()
{
LogEvents = new ObservableCollectionEx<LogEvent>();
patternMatching = new Regex(matchExpression, RegexOptions.Singleline | RegexOptions.Compiled);
}
/// <summary>
///
/// </summary>
public void Parse()
{
Task task = Task.Factory.StartNew(() => { InternalParse(); });
}
/// <summary>
///
/// </summary>
private void InternalParse()
{
OnSerilogParserProgressChanged?.Invoke(0);
try
{
if (!string.IsNullOrWhiteSpace(FilePath))
{
Console.WriteLine("Starting parse for {0}", FilePath);
long currentLength = 0;
FileInfo fi = new FileInfo(FilePath);
if (fi.Exists)
{
Console.WriteLine("Parsing Serilog file: {0}.", FilePath);
fi.Refresh();
List<LogEvent> parsedLogEvents = new List<LogEvent>();
StringBuilder sb = new StringBuilder();
using (FileStream fileStream = fi.Open(FileMode.Open, FileAccess.Read, FileShare.Write))
using (var streamReader = new StreamReader(fileStream))
{
while (streamReader.Peek() != -1)
{
sb.Append(streamReader.ReadLine());
LogEvent newLogEvent = ParseLogEvent(sb.ToString());
if (newLogEvent != null)
{
parsedLogEvents.Add(newLogEvent);
lastLogEvent = newLogEvent;
}
OnSerilogParserProgressChanged?.Invoke((int)(currentLength * 100 / fi.Length));
currentLength = currentLength + sb.ToString().Length;
sb.Clear();
}
}
LogEvents.ReplaceContent(parsedLogEvents);
}
Console.WriteLine("Finished parsing Serilog {0}.", FilePath);
}
}
catch (Exception ex)
{
Console.WriteLine("Error parsing Serilog." + ex.Message);
}
OnSerilogParserProgressChanged?.Invoke(100);
SerilogParserFinishedHandler onSerilogParserFinished = OnSerilogParserFinished;
if (onSerilogParserFinished == null)
return;
OnSerilogParserFinished();
}
/// <summary>
///
/// </summary>
/// <param name="mes"></param>
/// <returns></returns>
private LogEvent ParseLogEvent(string mes)
{
LogEvent logEvent = new LogEvent();
Match matcher = patternMatching.Match(mes);
try
{
if (matcher.Success)
{
logEvent.Message = matcher.Groups["Message"].Value;
DateTime dt;
if (!DateTime.TryParse(matcher.Groups["DateTime"].Value, out dt))
{
Console.WriteLine("Failed to parse date {Value}", matcher.Groups["DateTime"].Value);
}
logEvent.DateTime = dt;
logEvent.Level = matcher.Groups["Level"].Value;
logEvent.MachineName = matcher.Groups["MachineName"].Value;
logEvent.Source = matcher.Groups["Source"].Value;
}
else
{
if ((string.IsNullOrEmpty(mes) || (!Char.IsDigit(mes[0])) || !Char.IsDigit(mes[1])) && lastLogEvent != null)
{
// seems to be a continuation of the previous line, add it to the last event.
lastLogEvent.Message += Environment.NewLine;
lastLogEvent.Message += mes;
logEvent = null;
}
else
{
Console.WriteLine("Message parsing failed.");
}
if (logEvent != null)
logEvent.Message = mes;
}
}
catch (Exception ex)
{
Console.WriteLine("ParseLogEvent exception." + ex.Message);
}
return logEvent;
}
#region INotify
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string p) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(p));
#endregion
}
}
I have an ObservableCollectionEx class that extends the default ObservableCollection, this class suppresses the collection changed event until all the items have been added/replaced.
/// <summary>
/// Adds the supplied items to the collection and raises a single <see cref="CollectionChanged"/> event
/// when the operation is complete.
/// </summary>
/// <param name="items">The items to add.</param>
public void AddRange(IEnumerable<T> items, bool notifyAfter = true)
{
if (null == items)
{
throw new ArgumentNullException("items");
}
if (items.Any())
{
try
{
SuppressChangeNotification();
CheckReentrancy();
foreach (var item in items)
{
Add(item);
}
}
finally
{
if (notifyAfter)
FireChangeNotification();
suppressOnCollectionChanged = false;
}
}
}
/// <summary>
/// Replaces the content of the collection with the supplied items and raises a single <see cref="CollectionChanged"/> event
/// when the operation is complete.
/// </summary>
/// <param name="items">The items to replace the current content.</param>
public void ReplaceContent(IEnumerable<T> items)
{
SuppressChangeNotification();
ClearItems();
AddRange(items);
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!suppressOnCollectionChanged)
{
#if NoCrossThreadSupport
base.OnCollectionChanged(e);
#else
using (BlockReentrancy())
{
NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
if (eventHandler == null)
return;
Delegate[] delegates = eventHandler.GetInvocationList();
// Walk the invocation list
foreach (NotifyCollectionChangedEventHandler handler in delegates)
{
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
// If the subscriber is a DispatcherObject and different thread
if (dispatcherObject != null && !dispatcherObject.CheckAccess())
{
// Invoke handler in the target dispatcher's thread
dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, e);
}
else // Execute handler as is
handler(this, e);
}
}
#endif
}
}
I have tried using a List but I got the same behaviour.
Any ideas?

Convert Spellbox to a RichTextBox

Hans Passant has code that adds a textbox based spellbox for spell checking in WinForms app. It works great but I would like to know if it could be converted to a RichTextBox so that I could add bolding, color, etc.
using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Forms.Design;
[Designer(typeof(ControlDesigner))]
//[DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.Design.Serialization.CodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
class SpellBox : ElementHost
{
public SpellBox()
{
// string templatePath = System.Environment.GetEnvironmentVariable("USERPROFILE") + "\\Documents\\AviationMaintenanceLogger\\Templates\\";
box = new TextBox();
base.Child = box;
box.TextChanged += (s, e) => OnTextChanged(EventArgs.Empty);
Uri lex_file = new Uri(System.Environment.GetEnvironmentVariable("USERPROFILE") + "\\Documents\\AviationMaintenanceLogger\\Templates\\AML.lex");
box.SpellCheck.CustomDictionaries.Add(lex_file);
box.SpellCheck.IsEnabled = true;
box.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
this.WordWrap = true;
this.Size = new System.Drawing.Size(100, 20);
}
public override string Text
{
get { return box.Text; }
set { box.Text = value; }
}
[DefaultValue(false)]
public bool Multiline
{
get { return box.AcceptsReturn; }
set { box.AcceptsReturn = value; }
}
[DefaultValue(false)]
public bool WordWrap
{
get { return box.TextWrapping != TextWrapping.NoWrap; }
set { box.TextWrapping = value ? TextWrapping.Wrap : TextWrapping.NoWrap; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new System.Windows.UIElement Child
{
get { return base.Child; }
set { /* Do nothing to solve a problem with the serializer !! */ }
}
private TextBox box;
}
This is the new code that works:
using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Forms.Design;
using System.Windows.Documents;
using System.Drawing;
[Designer(typeof(ControlDesigner))]
//[DesignerSerializer("System.Windows.Forms.Design.ControlCodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.Design.Serialization.CodeDomSerializer, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
class SpellBoxNew : ElementHost
{
public SpellBoxNew()
{
// string templatePath = System.Environment.GetEnvironmentVariable("USERPROFILE") + "\\Documents\\AviationMaintenanceLogger\\Templates\\";
box = new RichTextBox();
base.Child = box;
box.IsReadOnly = false;
box.TextChanged += (s, e) => OnTextChanged(EventArgs.Empty);
Uri lex_file = new Uri(System.Environment.GetEnvironmentVariable("USERPROFILE") + "\\Documents\\AviationMaintenanceLogger\\Templates\\AML.lex");
box.SpellCheck.CustomDictionaries.Add(lex_file);
box.SpellCheck.IsEnabled = true;
box.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
//this.Font = new Font("Arial", 24,FontStyle.Bold);
// this.WordWrap = true;
this.Multiline = true;
this.Size = new System.Drawing.Size(100, 20);
}
[DefaultValue(false)]
public override string Text
{
get
{
// string richText = new TextRange(transcriberArea.Document.ContentStart, transcriberArea.Document.ContentEnd).Text;
string richText = new TextRange(box.Document.ContentStart, box.Document.ContentEnd).Text;
return richText;
}
set
{
box.Document.Blocks.Clear();
box.Document.Blocks.Add(new System.Windows.Documents.Paragraph(new Run(value)));
}
}
public bool Multiline
{
get { return box.AcceptsReturn; }
set { box.AcceptsReturn = value; }
}
[DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new System.Windows.UIElement Child
{
get { return base.Child; }
set { /* Do nothing to solve a problem with the serializer !! */ }
}
private RichTextBox box;

progress bar is updated after reaching 100% in c#

I am working on file transfer software. But progress bar shows 100% directly not showing actual progress. So do i need to change my method to write file. Or there is some silly mistake. Here is my code.
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Media;
using System.Threading;
namespace Sender2
{
public partial class Form1 : Form
{
private Thread thrDownload;
private static int PercentProgress;
private delegate void UpdateProgessCallback(Int64 BytesRead, Int64 TotalBytes);
public Form1()
{
InitializeComponent();
}
private void Browse_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
txtSelectFilePath.Text = openFileDialog1.FileName;
String path1=txtSelectFilePath.Text;
files_list.Items.Add(path1);
files_list.View = View.List;
}
}
private void UpdateProgress(Int64 BytesRead, Int64 TotalBytes)
{
PercentProgress = Convert.ToInt32((BytesRead * 100) / TotalBytes);
progressBar1.Value = PercentProgress;
lblProgress.Text = "Downloaded " + BytesRead + " out of " + TotalBytes + " (" + PercentProgress + "%)";
}
private void Send_Click(object sender, EventArgs e)
{
TransferService2.TransferService2Client client = new TransferService2.TransferService2Client();
foreach(ListViewItem item in files_list.Items)
{
TransferService2.File file = client.DownloadDocument(item.Text);
System.IO.FileStream fs = new System.IO.FileStream(#"c:\DownloadedFiles\" + file.Name, System.IO.FileMode.Create);
Int64 fileSize = file.Content.Length;
int bytesSize = 0;
byte[] downBuffer = new byte[2048];
int pos = 0;
int length = 128;
while (pos < file.Content.Length)
{
if (length > (file.Content.Length - pos))
{
length = file.Content.Length - pos;
}
fs.Write(file.Content, pos, length);
this.Invoke(new UpdateProgessCallback(this.UpdateProgress), new object[] { file.Content.Length, fileSize });
pos = pos + length;
}
MessageBox.Show(file.Name + " is downloaded");
}
}
}
}
TransferService2.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace TransferService2
{
public class TransferService2 : ITransferService2
{
public File DownloadDocument(String filepath)
{
File file = new File();
String path = filepath;
byte[] buffer;
FileStream fs = new FileStream(#path, FileMode.Open, FileAccess.Read);
try
{
int length = (int)fs.Length;
buffer = new byte[length];
int count;
int sum = 0;
while((count=fs.Read(buffer,sum,length-sum))>0)
{
sum = sum + count;
}
}
finally
{
fs.Close();
}
file.Content = buffer;
file.Name = Path.GetFileName(path);
return file;
}
}
}
ITransferService2.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace TransferService2
{
[ServiceContract]
public interface ITransferService2
{
[OperationContract]
File DownloadDocument(String filepath);
}
[DataContract]
public class File
{
[DataMember]
public string Name { get; set; }
[DataMember]
public byte[] Content { get; set; }
}
}
As I mentioned in the comments, you are performing the whole operation synchronously on the UI thread, hence it's busy and cannot update during the processing.
You need to let the operation execute asynchronously. There are many ways to do that, here is one possible way using async/await:
private async void Send_Click(object sender, EventArgs e)
{
TransferService2.TransferService2Client client = new TransferService2.TransferService2Client();
foreach(ListViewItem item in files_list.Items)
{
TransferService2.File file = await Task.Run(() => client.DownloadDocument(item.Text));
System.IO.FileStream fs = new System.IO.FileStream(#"c:\DownloadedFiles\" + file.Name, System.IO.FileMode.Create);
Int64 fileSize = file.Content.Length;
int bytesSize = 0;
byte[] downBuffer = new byte[2048];
int pos = 0;
int length = 128;
while (pos < file.Content.Length)
{
if (length > (file.Content.Length - pos))
{
length = file.Content.Length - pos;
}
await fs.WriteAsync(file.Content, pos, length);
// Here the await will resume on the UI thread, so no Invoke is needed
pos = pos + length;
this.UpdateProgress(pos, fileSize);
}
MessageBox.Show(file.Name + " is downloaded");
}
}

How can I get a CheckEdit in a NavBarGroup

We need to place a check box (and caption for it) in the header of a NavBarGroup. Is there a way to do this?
We created a NavBarGroupChecked class (NavBarGroupChecked.cs) that inherits from NavBarGroup and can just be dropped in to replace it. It adds a RepositoryItemCheckEdit member that tracks the checkbox and implements custom draw. It has a Checked property that tells you if it is checked and an event that will be called when the Checked status changes. That's pretty much it. Just drops in and works.
Code is below and also downloadable here.
// built from http://www.devexpress.com/example=E2061
using System;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using DevExpress.XtraEditors.Drawing;
using DevExpress.XtraEditors.Repository;
using DevExpress.XtraEditors.ViewInfo;
using DevExpress.XtraNavBar;
using DevExpress.XtraNavBar.ViewInfo;
namespace AutoTagCore.net.windward.controls
{
/// <summary>
/// A NavBarGroup that has a check box (with caption) in its header.
/// </summary>
public class NavBarGroupChecked : NavBarGroup
{
/// <summary>
/// Occurs when the Checked property value has been changed.
/// </summary>
public event EventHandler CheckedChanged;
private const int CHECK_BOX_WIDTH = 15;
private bool isLocked;
private RepositoryItemCheckEdit _GroupEdit;
private NavBarControl _NavBarControl;
private Rectangle hotRectangle;
/// <summary>
/// Initializes a new instance of the <see cref="T:DevExpress.XtraNavBar.NavBarGroup"/> class, with the specified caption.
/// </summary>
/// <param name="caption">A string representing the NavBar group's caption.</param>
public NavBarGroupChecked(string caption)
: base(caption)
{
ctor();
}
private void ctor()
{
GroupEdit = new RepositoryItemCheckEdit { GlyphAlignment = DevExpress.Utils.HorzAlignment.Far };
GroupEdit.Appearance.Options.UseTextOptions = true;
GroupEdit.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far;
GroupEdit.GlyphAlignment = DevExpress.Utils.HorzAlignment.Far;
ItemChanged += NavBarGroupChecked_ItemChanged;
}
private void NavBarGroupChecked_ItemChanged(object sender, System.EventArgs e)
{
if (NavBar != NavBarControl)
NavBarControl = NavBar;
}
/// <summary>
/// Creates an instance of the <see cref="T:DevExpress.XtraNavBar.NavBarGroup"/> class.
/// </summary>
public NavBarGroupChecked()
{
ctor();
}
/// <summary>
/// The NavBarControl that owns this. This must be set to work.
/// </summary>
private NavBarControl NavBarControl
{
get { return _NavBarControl; }
set { UnsubscribeEvents(value); _NavBarControl = value; SubscribeEvents(value); }
}
private void SubscribeEvents(NavBarControl navBarControl)
{
if (navBarControl == null)
return;
NavBarControl.CustomDrawGroupCaption += NavBarControl_CustomDrawGroupCaption;
NavBarControl.MouseClick += NavBarControl_MouseClick;
}
private void UnsubscribeEvents(NavBarControl navBarControl)
{
if (navBarControl != null)
return;
NavBarControl.CustomDrawGroupCaption -= NavBarControl_CustomDrawGroupCaption;
NavBarControl.MouseClick -= NavBarControl_MouseClick;
}
/// <summary>
/// true if the box is checked.
/// </summary>
public bool Checked { get; set; }
/// <summary>
/// The indent of the check box for the end of the header.
/// </summary>
public int CheckIndent { get; set; }
///<summary>
/// The check box displayed in the header.
///</summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public RepositoryItemCheckEdit GroupEdit
{
get { return _GroupEdit; }
set { _GroupEdit = value; }
}
private Rectangle GetCheckBoxBounds(Rectangle fixedCaptionBounds)
{
return new Rectangle(fixedCaptionBounds.Right - CHECK_BOX_WIDTH - CheckIndent, fixedCaptionBounds.Top, CHECK_BOX_WIDTH, fixedCaptionBounds.Height);
}
private bool IsCustomDrawNeeded(NavBarGroup group)
{
return GroupEdit != null && NavBarControl != null && !isLocked && group == this;
}
private void NavBarControl_CustomDrawGroupCaption(object sender, CustomDrawNavBarElementEventArgs e)
{
NavGroupInfoArgs infoArgs = (NavGroupInfoArgs) e.ObjectInfo;
if (!IsCustomDrawNeeded(infoArgs.Group))
return;
try
{
isLocked = true;
BaseNavGroupPainter painter = NavBarControl.View.CreateGroupPainter(NavBarControl);
Rectangle checkBoxBounds = GetCheckBoxBounds(infoArgs.CaptionBounds);
painter.DrawObject(infoArgs);
DrawCheckBox(e.Graphics, checkBoxBounds);
e.Handled = true;
}
finally
{
isLocked = false;
}
}
private void DrawCheckBox(Graphics g, Rectangle r)
{
BaseEditPainter painter = GroupEdit.CreatePainter();
BaseEditViewInfo info = GroupEdit.CreateViewInfo();
info.EditValue = Checked;
SizeF textBounds = info.Appearance.CalcTextSize(g, GroupEdit.Caption, 500);
int totalWidth = (int)textBounds.Width + r.Width + 10;
info.Bounds = new Rectangle(r.Right - totalWidth, r.Y, totalWidth, r.Height);
info.CalcViewInfo(g);
ControlGraphicsInfoArgs args = new ControlGraphicsInfoArgs(info, new DevExpress.Utils.Drawing.GraphicsCache(g), r);
painter.Draw(args);
args.Cache.Dispose();
}
private static NavBarViewInfo GetNavBarView(NavBarControl NavBar)
{
PropertyInfo pi = typeof(NavBarControl).GetProperty("ViewInfo", BindingFlags.Instance | BindingFlags.NonPublic);
return pi.GetValue(NavBar, null) as NavBarViewInfo;
}
private bool IsCheckBox(Point p)
{
NavBarHitInfo hi = NavBarControl.CalcHitInfo(p);
if (hi.Group == null || hi.Group != this)
return false;
NavBarViewInfo vi = GetNavBarView(NavBarControl);
vi.Calc(NavBarControl.ClientRectangle);
NavGroupInfoArgs groupInfo = vi.GetGroupInfo(hi.Group);
Rectangle checkBounds = GetCheckBoxBounds(groupInfo.CaptionBounds);
hotRectangle = checkBounds;
return checkBounds.Contains(p);
}
private void NavBarControl_MouseClick(object sender, MouseEventArgs e)
{
if (!IsCheckBox(e.Location))
return;
Checked = !Checked;
NavBarControl.Invalidate(hotRectangle);
if (CheckedChanged != null)
CheckedChanged(sender, e);
}
}
}
I had some issues with the way that solution worked and made some minor tweaks. See below.
A sample on how to use this is;
private void Form1_Load(object sender, EventArgs e)
{
var item = navBarControl1.Groups.Add(new NavBarGroupChecked("NavBarGroupCheckbox", navBarControl1)) as NavBarGroupChecked;
item.Hint = "my hint";
item.CheckedChanged += checkchanged;
}
private void checkchanged(object sender, EventArgs e)
{
MessageBox.Show("It Changed");
}
This is the solution:
// built from http://www.devexpress.com/example=E2061
using System;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using DevExpress.XtraEditors.Drawing;
using DevExpress.XtraEditors.Repository;
using DevExpress.XtraEditors.ViewInfo;
using DevExpress.XtraNavBar;
using DevExpress.XtraNavBar.ViewInfo;
namespace NavBarCheckTest
{
/// <summary>
/// A NavBarGroup that has a check box (with caption) in its header.
/// </summary>
public class NavBarGroupChecked : NavBarGroup
{
/// <summary>
/// Occurs when the Checked property value has been changed.
/// </summary>
public event EventHandler CheckedChanged;
private const int CHECK_BOX_WIDTH = 15;
private bool isLocked;
private RepositoryItemCheckEdit _GroupEdit;
private NavBarControl _NavBarControl;
private Rectangle hotRectangle;
/// <summary>
/// Initializes a new instance of the <see cref="T:DevExpress.XtraNavBar.NavBarGroup"/> class, with the specified caption.
/// </summary>
/// <param name="caption">A string representing the NavBar group's caption.</param>
public NavBarGroupChecked(string caption, NavBarControl parent = null)
: base(caption)
{
ctor(parent);
}
private void ctor(NavBarControl parent = null)
{
GroupEdit = new RepositoryItemCheckEdit { GlyphAlignment = DevExpress.Utils.HorzAlignment.Far };
GroupEdit.Caption = string.Empty;
GroupEdit.Appearance.Options.UseTextOptions = true;
GroupEdit.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Far;
GroupEdit.GlyphAlignment = DevExpress.Utils.HorzAlignment.Far;
ItemChanged += NavBarGroupChecked_ItemChanged;
if (parent != null)
NavBarControl = parent;
}
private void NavBarGroupChecked_ItemChanged(object sender, System.EventArgs e)
{
if (NavBar != NavBarControl)
NavBarControl = NavBar;
}
/// <summary>
/// Creates an instance of the <see cref="T:DevExpress.XtraNavBar.NavBarGroup"/> class.
/// </summary>
public NavBarGroupChecked(NavBarControl parent = null)
{
ctor(parent);
}
/// <summary>
/// The NavBarControl that owns this. This must be set to work.
/// </summary>
private NavBarControl NavBarControl
{
get { return _NavBarControl; }
set { UnsubscribeEvents(value); _NavBarControl = value; SubscribeEvents(value); }
}
private void SubscribeEvents(NavBarControl navBarControl)
{
if (navBarControl == null)
return;
NavBarControl.CustomDrawGroupCaption += NavBarControl_CustomDrawGroupCaption;
NavBarControl.MouseClick += NavBarControl_MouseClick;
}
private void UnsubscribeEvents(NavBarControl navBarControl)
{
if (navBarControl != null)
return;
NavBarControl.CustomDrawGroupCaption -= NavBarControl_CustomDrawGroupCaption;
NavBarControl.MouseClick -= NavBarControl_MouseClick;
}
/// <summary>
/// true if the box is checked.
/// </summary>
public bool Checked { get; set; }
/// <summary>
/// The indent of the check box for the end of the header.
/// </summary>
public int CheckIndent { get; set; }
///<summary>
/// The check box displayed in the header.
///</summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public RepositoryItemCheckEdit GroupEdit
{
get { return _GroupEdit; }
set { _GroupEdit = value; }
}
private int GetCheckBoxWidth()
{
return CheckIndent * 2 + 10;
}
//private Rectangle GetCaptionBounds(Rectangle originalCaptionBounds)
//{
// return new Rectangle(originalCaptionBounds.Location, new Size(originalCaptionBounds.Width - GetCheckBoxWidth(), originalCaptionBounds.Height));
//}
private Rectangle GetCheckBoxBounds(Rectangle fixedCaptionBounds)
{
return new Rectangle(fixedCaptionBounds.Right - CHECK_BOX_WIDTH - CheckIndent, fixedCaptionBounds.Top, CHECK_BOX_WIDTH, fixedCaptionBounds.Height);
//return new Rectangle(fixedCaptionBounds.Right, fixedCaptionBounds.Top, GetCheckBoxWidth(), fixedCaptionBounds.Height); ;
}
private bool IsCustomDrawNeeded(NavBarGroup group)
{
return GroupEdit != null && NavBarControl != null && !isLocked && group == this;
}
private void NavBarControl_CustomDrawGroupCaption(object sender, CustomDrawNavBarElementEventArgs e)
{
NavGroupInfoArgs infoArgs = (NavGroupInfoArgs) e.ObjectInfo;
if (!IsCustomDrawNeeded(infoArgs.Group))
return;
try
{
isLocked = true;
BaseNavGroupPainter painter = NavBarControl.View.CreateGroupPainter(NavBarControl);
Rectangle originalCaptionBounds = new Rectangle(infoArgs.CaptionClientBounds.X, infoArgs.CaptionClientBounds.Y, infoArgs.CaptionClientBounds.Width - infoArgs.ButtonBounds.Width, infoArgs.CaptionClientBounds.Height);
Rectangle checkBoxBounds = GetCheckBoxBounds(originalCaptionBounds);
painter.DrawObject(infoArgs);
DrawCheckBox(e.Graphics, checkBoxBounds);
e.Handled = true;
}
finally
{
isLocked = false;
}
}
private void DrawCheckBox(Graphics g, Rectangle r)
{
BaseEditPainter painter = GroupEdit.CreatePainter();
BaseEditViewInfo info = GroupEdit.CreateViewInfo();
info.EditValue = Checked;
SizeF textBounds = info.Appearance.CalcTextSize(g, GroupEdit.Caption, 500);
int totalWidth = (int)textBounds.Width + r.Width + 10;
info.Bounds = new Rectangle(r.Right - totalWidth, r.Y, totalWidth, r.Height);
info.CalcViewInfo(g);
ControlGraphicsInfoArgs args = new ControlGraphicsInfoArgs(info, new DevExpress.Utils.Drawing.GraphicsCache(g), r);
painter.Draw(args);
args.Cache.Dispose();
}
private static NavBarViewInfo GetNavBarView(NavBarControl NavBar)
{
PropertyInfo pi = typeof(NavBarControl).GetProperty("ViewInfo", BindingFlags.Instance | BindingFlags.NonPublic);
return pi.GetValue(NavBar, null) as NavBarViewInfo;
}
private bool IsCheckBox(Point p)
{
NavBarHitInfo hi = NavBarControl.CalcHitInfo(p);
if (hi.Group == null || hi.Group != this)
return false;
NavBarViewInfo vi = GetNavBarView(NavBarControl);
vi.Calc(NavBarControl.ClientRectangle);
NavGroupInfoArgs groupInfo = vi.GetGroupInfo(hi.Group);
Rectangle originalCaptionBounds = new Rectangle(groupInfo.CaptionClientBounds.X, groupInfo.CaptionClientBounds.Y, groupInfo.CaptionClientBounds.Width - groupInfo.ButtonBounds.Width, groupInfo.CaptionClientBounds.Height);
Rectangle checkBounds = GetCheckBoxBounds(originalCaptionBounds);
hotRectangle = checkBounds;
return checkBounds.Contains(p);
}
private void NavBarControl_MouseClick(object sender, MouseEventArgs e)
{
if (!IsCheckBox(e.Location))
return;
Checked = !Checked;
NavBarControl.Invalidate(hotRectangle);
if (CheckedChanged != null)
CheckedChanged(sender, e);
}
}
}

VisualCollection and ContentPropertyAttribute in XAML

I want to write a custom FrameworkElement which host Visuals. My first attempt was to create an instance of ContainerVisual and write a wrapper property for ContainerVisual.Children and then set it as ContentProperty so I can and Visuals via XAML. But VisualCollection does only implement ICollection and not IList or any supported interface and VisualCollection is selead so I can't implement IList on my own.
How can I hostvisuals and let them add declaratively using XAML?
Okay, long time ago but here is the solution I found that time back...
The Collection:
Note the hack comments.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Collections.ObjectModel;
using WPF.Controls.Primitives;
namespace WPF.Controls.Core
{
public class PrimitiveCollection : ObservableCollection<Primitive>
{
protected PrimitiveContainerVisual _owner;
public PrimitiveCollection(PrimitiveContainerVisual owner)
: base()
{
if (owner == null)
throw new ArgumentNullException("owner");
_owner = owner;
}
protected override void ClearItems()
{
foreach (var item in this)
{
item.IsDirtyChanged -= new IsDirtyChangedHandler(item_IsDirtyChanged);
_owner.InternalRemoveVisualChild(item);
}
base.ClearItems();
}
protected override void InsertItem(int index, Primitive item)
{
if (item != null && item.Parent != null)
throw new ArgumentNullException("Visual has parent");
item.IsDirtyChanged += new IsDirtyChangedHandler(item_IsDirtyChanged);
_owner.InternalAddVisualChild(item);
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
Primitive item = this[index];
item.IsDirtyChanged -= new IsDirtyChangedHandler(item_IsDirtyChanged);
_owner.InternalRemoveVisualChild(item);
base.RemoveItem(index);
}
protected override void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
}
void item_IsDirtyChanged(object sender, PrimitiveChangedEventArgs e)
{
if(e.IsDirty)
_owner.RequestRedraw();
}
}
}
And the Control which you can use in XAML
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using WPF.Controls.Primitives;
using System.Windows;
using System.Reflection;
namespace WPF.Controls.Core
{
public class PrimitiveContainerVisual : Visual
{
private PrimitiveCollection _primitives;
private PropertyInfo _contentBoundsPropInfo;
private PropertyInfo _descendantBoundsPropInfo;
public PrimitiveCollection Children
{
get { return _primitives; }
set { _primitives = value; }
}
public Rect ContentBounds
{
// HACK access internal property of Visual
get { return (Rect)_contentBoundsPropInfo.GetValue(this, null); }
}
public Rect DescendantBounds
{
// HACK access internal property of Visual
get { return (Rect)_descendantBoundsPropInfo.GetValue(this, null); }
}
public PrimitiveContainerVisual()
{
_primitives = new PrimitiveCollection(this);
Type thisType = this.GetType();
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
_contentBoundsPropInfo = thisType.GetProperty("VisualContentBounds", flags);
_descendantBoundsPropInfo = thisType.GetProperty("VisualDescendantBounds", flags);
}
internal void InternalAddVisualChild(Primitive prim)
{
this.AddVisualChild(prim);
}
internal void InternalRemoveVisualChild(Primitive prim)
{
this.RemoveVisualChild(prim);
}
public bool RequestRedraw()
{
UIElement uiParent = VisualParent as UIElement;
if (uiParent != null)
{
uiParent.InvalidateVisual();
return true;
}
else
return false;
}
}
}

Resources