I have created an app using Xamarin to help watching movies online. It shows the subtitles on top of all other windows. This has been done using the NSPanel, as it was the only way to make it work on MacOS Mojave.
The app works well. Now I want to improve the app by making NSPanel respond to the keyboard events, so I can control the app by using the keyboard for pausing, playing, going backward or going forward.
How do I get keyboard events in the topmost NSPanel?
I tried to use this code:
NSEvent.AddLocalMonitorForEventsMatchingMask(NSEventMask.KeyDown, KeyboardEventHandler);
private static NSEvent KeyboardEventHandler(NSEvent keyEvent)
{
// handle key down events here
return (keyEvent);
}
But it only works when the app is not in the full-screen mode.
The full SubtitlesViewer-MACOS project can be found here.
Here is the part of the code that creates the panel:
public override void ViewWillAppear()
{
base.ViewWillAppear();
SetupView();
}
private void SetupView()
{
var screenRes = screenResolution();
int PANEL_HEIGHT = 200;
subtitlesPanel = new NSPanel
(
new CoreGraphics.CGRect(40, 50, screenRes.Width - 80, PANEL_HEIGHT),
NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Miniaturizable | NSWindowStyle.DocModal,
NSBackingStore.Buffered, true
)
{
BackgroundColor = NSColor.FromCalibratedRgba(0, 0, 0, 0.0f),
ReleasedWhenClosed = true,
HidesOnDeactivate = false,
FloatingPanel = true,
StyleMask = NSWindowStyle.NonactivatingPanel,
Level = NSWindowLevel.MainMenu - 1,
IsMovable = true,
CollectionBehavior = NSWindowCollectionBehavior.CanJoinAllSpaces |
NSWindowCollectionBehavior.FullScreenAuxiliary
};
subtitlesPanel.OrderFront(null);
subtitleTextButton = new NSButton(new CoreGraphics.CGRect(40, 0, screenRes.Width - 120, PANEL_HEIGHT-30))
{
Title = "",
WantsLayer = true
};
subtitleTextButton.Layer.BackgroundColor = NSColor.Clear.CGColor;
subtitleTextField = new NSTextField(new CoreGraphics.CGRect(40, 0, screenRes.Width - 120, PANEL_HEIGHT-30))
{
Alignment = NSTextAlignment.Center
};
subtitleTextField.Cell.Alignment = NSTextAlignment.Center;
forwardButton = new NSButton(new CoreGraphics.CGRect(0, 0, 40, 30));
forwardButton.Title = ">>";
forwardButton.Activated += (object sender, EventArgs e) => {
subtitlesProvider.Forward();
};
backButton = new NSButton(new CoreGraphics.CGRect(0, 30, 40, 30));
backButton.Title = "<<";
backButton.Activated += (object sender, EventArgs e) => {
subtitlesProvider.Back();
};
startStopButton = new NSButton(new CoreGraphics.CGRect(0, 60, 40, 30));
startStopButton.Title = "Play";
startStopButton.Activated += (object sender, EventArgs e) => {
subtitlesProvider.StartStop(subtitlesProvider.Playing);
};
subtitlesPanel.ContentView.AddSubview(subtitleTextButton, NSWindowOrderingMode.Below, null);
subtitlesPanel.ContentView.AddSubview(subtitleTextField, NSWindowOrderingMode.Below, null);
subtitlesPanel.ContentView.AddSubview(forwardButton, NSWindowOrderingMode.Below, null);
subtitlesPanel.ContentView.AddSubview(backButton, NSWindowOrderingMode.Below, null);
subtitlesPanel.ContentView.AddSubview(startStopButton, NSWindowOrderingMode.Below, null);
SetupSubtitlesProvider();
}
Please kindly advice what else should I try to make it work.
I have found the solution here:
keyDown not being called
This is my implementation of NSPanelExt class to handle the keys.
public class NSPanelExt : NSPanel
{
public KeyPressedHandler KeyPressed;
public delegate void KeyPressedHandler(KeyCodeEventArgs e);
public NSPanelExt(CGRect contentRect, NSWindowStyle aStyle, NSBackingStore bufferingType, bool deferCreation) : base(contentRect, aStyle, bufferingType, deferCreation)
{
}
public override bool CanBecomeMainWindow => true;
public override bool CanBecomeKeyWindow => true;
public override bool AcceptsFirstResponder()
{
return true;
}
public override void KeyDown(NSEvent theEvent)
{
// this function is never called
KeyPressed?.Invoke(new KeyCodeEventArgs { Key = GetKeyCode(theEvent.KeyCode) });
}
private KeyCode GetKeyCode(ushort keyCode)
{
KeyCode result = KeyCode.Unknown;
switch (keyCode)
{
case 123:
result = KeyCode.Left;
break;
case 49:
result = KeyCode.Space;
break;
case 124:
result = KeyCode.Right;
break;
case 53:
result = KeyCode.Esc;
break;
}
return result;
}
I have also updated the ViewController to keep NSPanel always active.
public partial class ViewController : NSViewController
{
// ...
private NSButton startStopButton;
Timer _timer = new Timer();
private void SetupView()
{
// ...
subtitlesPanel.KeyPressed += SubtitlesPanel_KeyPressed;
// ...
IntializeKeepWindowFocusedTimer();
}
void SubtitlesPanel_KeyPressed(KeyCodeEventArgs e)
{
switch(e.Key)
{
case KeyCode.Left:
backButton.PerformClick(this);
break;
case KeyCode.Right:
forwardButton.PerformClick(this);
break;
case KeyCode.Space:
startStopButton.PerformClick(this);
break;
case KeyCode.Esc:
_timer.Stop();
break;
}
}
private void IntializeKeepWindowFocusedTimer()
{
_timer.Interval = 200; //in milliseconds
_timer.Elapsed += Timer_Elapsed;;
_timer.AutoReset = true;
_timer.Enabled = true;
}
void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
NSApplication.SharedApplication.BeginInvokeOnMainThread(() =>
{
subtitlesPanel.MakeKeyWindow();
if (SetSubtitleNeeded)
{
subtitlesProvider.SetSubTitle(0);
startStopButton.Title = "Stop";
SetSubtitleNeeded = false;
_timer.Interval = 5000;
}
});
}
private bool SetSubtitleNeeded = false;
partial void ClickedButton(NSObject sender)
{
_timer.Stop();
var nsUrl = subtitleFileSelector.GetFile();
if (nsUrl == null)
return;
fileName = nsUrl.Path;
subtitlesProvider.ReadFromFile(fileName);
SetSubtitleNeeded = true;
_timer.Start();
}
Related
I am using the events ItemSelectionChanged and SelectedIndexChanged of the ListView class to answer this question.
The code is below, and the only bug that seems to remain is that I use the 250ms timer also when the user uses rubberband selection, not only for single or double click selection.
internal Form1()
{
InitializeComponent();
t.Tick += T_Tick;
}
internal bool santinela = false;
internal void T_Tick(object sender, EventArgs e)
{
t.Stop();
if (!santinela)
{
bool newVal = selectionChangedItem.Selected;
selectionChangedItem.Checked =
selectionChangedItem.Selected = newVal;
santinela = true;
}
}
internal Timer t = new Timer()
{
Interval = 250
};
internal bool santinela3 = true;
internal void listView1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (santinela3)
{
if (e.CurrentValue == CheckState.Checked)
{
listView1.SelectedIndices.Remove(e.Index);
}
else if (e.CurrentValue == CheckState.Unchecked)
{
listView1.SelectedIndices.Add(e.Index);
}
}
}
internal void listView1_ItemActivate(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count > 0)
{
MessageBox.Show(listView1.SelectedItems[0].Text);
}
}
internal ListViewItem selectionChangedItem = null;
internal bool santinela2 = true;
internal void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
if (!santinela2)
{
return;
}
if (t.Enabled)
{
santinela = true;
t.Stop();
// double click: both clicks must be done in the same place
if (e.Item == selectionChangedItem)
{
if (!e.IsSelected)
{
santinela2 = true;
e.Item.Selected = true;
santinela2 = false;
}
listView1_ItemActivate(sender, EventArgs.Empty);
}
selectionChangedItem = null;
}
else
{
santinela = false;
t.Stop();
selectionChangedItem = e.Item;
t.Start();
}
}
internal void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listView1.SelectedIndices.Count == 0)
{
santinela = true;
t.Stop();
selectionChangedItem = null;
santinela3 = false;
for (int i = 0; i < listView1.CheckedItems.Count; ++i)
{
listView1.CheckedItems[i].Checked = false;
}
santinela3 = true;
}
}
The relevant designer code is below:
System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem("item1");
System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem("item2");
this.listView1 = new System.Windows.Forms.ListView();
this.SuspendLayout();
//
// listView1
//
this.listView1.CheckBoxes = true;
listViewItem1.StateImageIndex = 0;
listViewItem2.StateImageIndex = 0;
this.listView1.Items.AddRange(new System.Windows.Forms.ListViewItem[] {
listViewItem1,
listViewItem2});
this.listView1.Location = new System.Drawing.Point(12, 12);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(401, 327);
this.listView1.TabIndex = 0;
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.ItemActivate += new System.EventHandler(this.listView1_ItemActivate);
this.listView1.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.listView1_ItemCheck);
this.listView1.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.listView1_ItemSelectionChanged);
this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged);
Update: The issue I am facing because of the timer is that after hovering with the rubberband a ListViewItem, there is the useless delay of the timer before the item gets checked. When the user resizes/moves the rubberband so that the ListViewItem is no longer checked, there is the same delay. If the user does not know of that non-standard delay, the selection can be wrong.
I am trying to run some source code that I downloaded from here
This is essentially an app that will allow users to manage contacts and calendar appointments etc.
I am currently experiencing 3 errors, all seemingly related...
CS1061 - UIImageView does not contain a definition for 'SetImage' and no extension method 'SetImage' accepting a first argument of type 'UIImageView' could be found (are you missing a using directive or an assembly reference?)
It seems that SetImage is not defined somewhere?
Would someone be able to tell me what i need to do to resolve the error. I post my code below...
using System;
using Foundation;
using UIKit;
using System.CodeDom.Compiler;
using MessageUI;
using Microsoft.Office365.OutlookServices;
using FiveMinuteMeeting.Shared.ViewModels;
using CoreGraphics;
using FiveMinuteMeeting.Shared;
namespace FiveMinuteMeeting.iOS
{
partial class ContactDetailViewController : UIViewController
{
public ContactDetailViewController(IntPtr handle)
: base(handle)
{
}
public DetailsViewModel ViewModel
{
get;
set;
}
UIBarButtonItem save;
public override void ViewDidLoad()
{
base.ViewDidLoad();
NavigationController.NavigationBar.BarStyle = UIBarStyle.Black;
save = new UIBarButtonItem(UIBarButtonSystemItem.Save,
async (sender, args) =>
{
ViewModel.FirstName = TextFirst.Text.Trim();
ViewModel.LastName = TextLast.Text.Trim();
ViewModel.Email = TextEmail.Text.Trim();
ViewModel.Phone = TextPhone.Text.Trim();
//BigTed.BTProgressHUD.Show("Saving contact...");
await ViewModel.SaveContact();
//BigTed.BTProgressHUD.Dismiss();
NavigationController.PopToRootViewController(true);
});
TextEmail.ShouldReturn += ShouldReturn;
TextFirst.ShouldReturn += ShouldReturn;
TextPhone.ShouldReturn += ShouldReturn;
TextLast.ShouldReturn += ShouldReturn;
TextEmail.ValueChanged += (sender, args) =>
{
ImagePhoto.SetImage(
url: new NSUrl(Gravatar.GetURL(TextEmail.Text, 172)),
placeholder: UIImage.FromBundle("missing.png")
);
};
var color = new CGColor(17.0F / 255.0F, 113.0F / 255.0F, 197.0F / 255F);
TextEmail.Layer.BorderColor = color;
TextFirst.Layer.BorderColor = color;
TextPhone.Layer.BorderColor = color;
TextLast.Layer.BorderColor = color;
ButtonCall.Clicked += (sender, args) => PlaceCall();
NSNotificationCenter.DefaultCenter.AddObserver
(UIKeyboard.DidShowNotification, KeyBoardUpNotification);
// Keyboard Down
NSNotificationCenter.DefaultCenter.AddObserver
(UIKeyboard.WillHideNotification, KeyBoardDownNotification);
double min = Math.Min((float)ImagePhoto.Frame.Width, (float)ImagePhoto.Frame.Height);
ImagePhoto.Layer.CornerRadius = (float)(min / 2.0);
ImagePhoto.Layer.MasksToBounds = false;
ImagePhoto.Layer.BorderColor = new CGColor(1, 1, 1);
ImagePhoto.Layer.BorderWidth = 3;
ImagePhoto.ClipsToBounds = true;
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (ViewModel == null)
{
ViewModel = new DetailsViewModel();
NavigationItem.RightBarButtonItem = save;
}
else
{
this.Title = ViewModel.FirstName;
TextEmail.Text = ViewModel.Email;
TextFirst.Text = ViewModel.FirstName;
TextLast.Text = ViewModel.LastName;
TextPhone.Text = ViewModel.Phone;
ImagePhoto.SetImage(
url: new NSUrl(Gravatar.GetURL(ViewModel.Contact.EmailAddresses[0].Address, 172)),
placeholder: UIImage.FromBundle("missing.png")
);
NavigationItem.RightBarButtonItem = null;
}
}
private bool ShouldReturn(UITextField field)
{
field.ResignFirstResponder();
return true;
}
private void PlaceCall()
{
var alertPrompt = new UIAlertView("Dial Number?",
"Do you want to call " + TextPhone.Text + "?",
null, "No", "Yes");
alertPrompt.Dismissed += (sender, e) =>
{
if ((int)e.ButtonIndex >= (int)alertPrompt.FirstOtherButtonIndex)
{
var url = new NSUrl("tel:" + TextPhone.Text);
if (!UIApplication.SharedApplication.OpenUrl(url))
{
var av = new UIAlertView("Not supported",
"Scheme 'tel:' is not supported on this device",
null,
"OK",
null);
av.Show();
}
else
{
UIApplication.SharedApplication.OpenUrl(url);
}
}
};
alertPrompt.Show();
}
/*private async void SendEmail()
{
var mailController = new MFMailComposeViewController();
mailController.SetToRecipients(new string[] { TextEmail.Text });
mailController.SetSubject("5 Minute Meeting");
mailController.SetMessageBody("We are having a 5 minute stand up tomorrow at this time! Check your calendar.", false);
mailController.Finished += (object s, MFComposeResultEventArgs args) =>
{
Console.WriteLine(args.Result.ToString());
args.Controller.DismissViewController(true, (Action)null);
};
PresentViewControllerAsync(mailController, true);
}*/
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
switch(segue.Identifier)
{
case "email":
{
var vc = segue.DestinationViewController as SendEmailViewController;
vc.ViewModel.FirstName = ViewModel.FirstName;
vc.ViewModel.LastName = ViewModel.LastName;
vc.ViewModel.Email = ViewModel.Email;
}
break;
case "meeting":
{
var vc = segue.DestinationViewController as NewEventDurationViewController;
vc.ViewModel.FirstName = ViewModel.FirstName;
vc.ViewModel.LastName = ViewModel.LastName;
vc.ViewModel.Email = ViewModel.Email;
}
break;
}
}
#region Keyboard
private UIView activeview; // Controller that activated the keyboard
private float scrollamount; // amount to scroll
private float bottom; // bottom point
private const float Offset = 68.0f; // extra offset
private bool moveViewUp; // which direction are we moving
private void KeyBoardDownNotification(NSNotification notification)
{
if (moveViewUp) { ScrollTheView(false); }
}
private void ScrollTheView(bool move)
{
// scroll the view up or down
UIView.BeginAnimations(string.Empty, System.IntPtr.Zero);
UIView.SetAnimationDuration(0.3);
CGRect frame = (CGRect)View.Frame;
if (move)
{
frame.Y -= scrollamount;
}
else
{
frame.Y += scrollamount;
scrollamount = 0;
}
View.Frame = frame;
UIView.CommitAnimations();
}
private void KeyBoardUpNotification(NSNotification notification)
{
// get the keyboard size
var r = (CGRect)UIKeyboard.FrameBeginFromNotification((NSNotification)notification);
// Find what opened the keyboard
foreach (UIView view in this.View.Subviews)
{
if (view.IsFirstResponder)
activeview = view;
}
// Bottom of the controller = initial position + height + offset
bottom = ((float)activeview.Frame.Y + (float)activeview.Frame.Height + Offset);
// Calculate how far we need to scroll
scrollamount = ((float)r.Height - ((float)View.Frame.Size.Height - bottom));
// Perform the scrolling
if (scrollamount > 0)
{
moveViewUp = true;
ScrollTheView(moveViewUp);
}
else
{
moveViewUp = false;
}
}
#endregion
}
}
I am very new to Xamarin & app development and I just wanted to run this because it is a very similar project to one that I am creating.
Thanks for your help
SetImage is a extension method contained in Xamarin.SDWebImage. Ensure, that you have restored all nuget packages or have installed it via
Install-Package Xamarin.SDWebImage
see: https://www.nuget.org/packages/Xamarin.SDWebImage/
Here a explanation of the error.
https://msdn.microsoft.com/en-us/library/bb383961.aspx
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
CS1061 - UIImageView does not contain a definition for 'SetImage' and no extension method 'SetImage' accepting a first argument of type 'UIImageView' could be found (are you missing a using directive or an assembly reference?)
In your code I don't watch a declaration of ImagePhoto check right click goto definition. And verify that for imagePhoto exist a method setImage ImagePhoto I'm asumming that is object of type UIImage
ImagePhoto.SetImage(
url: new NSUrl(Gravatar.GetURL(TextEmail.Text, 172)),
placeholder: UIImage.FromBundle("missing.png")
);
What I get is 99% and on the label8 I see 1 and not 0 and the progressBar should get to 100%.
This is the backgroundworker dowork and progresschanged and completed events:
private int numberofallmessages = 0;
private int countMsg = 0;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
OpenPop.Pop3.Pop3Client PopClient = new OpenPop.Pop3.Pop3Client();
PopClient.Connect("mail.bezeqint.net", 110, false);
PopClient.Authenticate("mymeila", "mypass",
OpenPop.Pop3.AuthenticationMethod.UsernameAndPassword);
int messageCount = PopClient.GetMessageCount();
numberofallmessages = messageCount;
allMessages = new List<OpenPop.Mime.Message>(messageCount);
for (int i = messageCount; i > 0; i--)
{
if (backgroundWorker1.CancellationPending == true)
{
e.Cancel = true;
return;
}
allMessages.Add(PopClient.GetMessage(i));
int nProgress = (messageCount - i + 1) * 100 / messageCount;
backgroundWorker1.ReportProgress(nProgress, PopClient.GetMessageCount().ToString() + "/" + i);
}
PopClient.Disconnect();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pbt.Value = e.ProgressPercentage;
pbt.Text = e.ProgressPercentage.ToString() + "%";
label8.Text = e.UserState.ToString();
label8.Visible = true;
lstMail.Items.Add(allMessages[countMsg].Headers.Subject);
countMsg += 1;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (closingForm)
this.Close();
}
In the constructor I start the backgroundworker and set the progressBar (pbt) to 0%.
backgroundWorker1.RunWorkerAsync();
pbt.Text = "0%";
This is the progressBar code:
In top of form1:
ProgressBarWithText pbt = new ProgressBarWithText();
In constructor:
pbt.Size = new Size(216, 10);
pbt.Location = new Point(8, 312);
groupBox1.Controls.Add(pbt);
The ProgressBarWithText class:
public class ProgressBarWithText : ProgressBar
{
const int WmPaint = 15;
SizeF TextSize;
PointF TextPos;
bool dontpaint = false;
public ProgressBarWithText()
{
this.DoubleBuffered = true;
this.TextChanged += ProgressBarWithText_TextChanged;
this.SizeChanged += ProgressBarWithText_SizeChanged;
}
public override string Text
{
get { return base.Text; }
set { base.Text = value; }
}
void RecalcTextPos()
{
if (this.IsDisposed == true)
return;
if (string.IsNullOrEmpty(base.Text))
return;
using (var graphics = Graphics.FromHwnd(this.Handle))
{
TextSize = graphics.MeasureString(base.Text, this.Font);
TextPos.X = (this.Width / 2) - (TextSize.Width / 2);
TextPos.Y = (this.Height / 2) - (TextSize.Height / 2);
}
}
void ProgressBarWithText_SizeChanged(object sender, EventArgs e)
{
RecalcTextPos();
}
void ProgressBarWithText_TextChanged(object sender, EventArgs e)
{
RecalcTextPos();
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
base.WndProc(ref m);
if (dontpaint == false)
{
switch (m.Msg)
{
case WmPaint:
using (var graphics = Graphics.FromHwnd(Handle))
graphics.DrawString(base.Text, base.Font, Brushes.Black, TextPos.X, TextPos.Y);
break;
}
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams result = base.CreateParams;
result.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return result;
}
}
}
The question is if I really downloaded all the emails or there is one left? And how to change/set the code is it will get to 100% and 0 emails downloaded count?
I did a test now I changed the line:
int messageCount = PopClient.GetMessageCount();
To
int messageCount = 5;
And I see in the listView I added to the designer that there are 5 emails.
But what I see in label8 is 7011/1 7011 is the overall emails in the account but for the test I'm downloading only 5. Again it stopped on 1 not 0.
And the progressBar stopped on 80% the text is 80% but I see the green color in the progressBar moved to the end to 100%.
Why it's stopping on 1 and why I see 80%?
I would like that when the form loads and/or starts my picture slide will start automatically.I tried to put the path of where the folder is located but it keeps giving an error. When I use it a dialog box it works. I am trying to bypass the dialog box so it starts automatically.
public partial class Form1 : Form
{
private string[] folderFile = null;
private int selected = 0;
private int end = 0;
FolderBrowserDialog folderBrowserDialog1 = new FolderBrowserDialog();
// The folder is pre created
string path1 = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "\\Pictures";
public Form1()
{
InitializeComponent();
//This does not work when the form starts up.
if (!Directory.Exists(path1))
{
string[] part1 = null, part2 = null, part3 = null;
part1 = Directory.GetFiles(path1, "*.jpg");
part2 = Directory.GetFiles(path1, "*.jpeg");
part3 = Directory.GetFiles(path1, "*.bmp");
folderFile = new string[part1.Length + part2.Length + part3.Length];
Array.Copy(part1, 0, folderFile, 0, part1.Length);
Array.Copy(part2, 0, folderFile, part1.Length, part2.Length);
Array.Copy(part3, 0, folderFile, part1.Length + part2.Length, part3.Length);
selected = 0;
//begin = 0;
end = folderFile.Length;
showImage(folderFile[selected]);
// 5 to 10 second intervals
//timer1.Enabled = true;
}
else
{
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void timer1_Tick(object sender, EventArgs e)
{
nextImage();
}
private void btnFolder_Click(object sender, EventArgs e)
{
//Original
//This works!!
//while (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
//{
// string[] part1 = null, part2 = null, part3 = null;
// part1 = Directory.GetFiles(folderBrowserDialog1.SelectedPath, "*.jpg");
// part2 = Directory.GetFiles(folderBrowserDialog1.SelectedPath, "*.jpeg");
// part3 = Directory.GetFiles(folderBrowserDialog1.SelectedPath, "*.bmp");
// folderFile = new string[part1.Length + part2.Length + part3.Length];
// Array.Copy(part1, 0, folderFile, 0, part1.Length);
// Array.Copy(part2, 0, folderFile, part1.Length, part2.Length);
// Array.Copy(part3, 0, folderFile, part1.Length + part2.Length, part3.Length);
// selected = 0;
// //begin = 0;
// end = folderFile.Length;
// showImage(folderFile[selected]);
// //btnPrev.Enabled = true;
// //btnNext.Enabled = true;
// //btnStartSlide.Enabled = true;
//}
}
private void showImage(string path)
{
Image imgtemp = Image.FromFile(path);
//pictureBox1.Width = imgtemp.Width / 2;
//pictureBox1.Height = imgtemp.Height / 2;
//pictureBox1.Image = imgtemp;
panel1.BackgroundImage = imgtemp;
}
private void prevImage()
{
if (selected == 0)
{
selected = folderFile.Length - 1;
showImage(folderFile[selected]);
}
else
{
selected = selected - 1;
showImage(folderFile[selected]);
}
}
private void nextImage()
{
if (selected == folderFile.Length - 1)
{
selected = 0;
showImage(folderFile[selected]);
}
else
{
selected = selected + 1;
showImage(folderFile[selected]);
}
}
private void btnPreviews_Click(object sender, EventArgs e)
{
prevImage();
}
private void btnNext_Click(object sender, EventArgs e)
{
nextImage();
}
private void btnStart_Click(object sender, EventArgs e)
{
if (timer1.Enabled == true)
{
timer1.Enabled = false;
btnStart.Text = "<< START >>";
}
else
{
timer1.Enabled = true;
btnStart.Text = "<< STOP >>";
}
}
}
}
Try
string path1 = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) + "\\Sample_Pictures";
or
string path1 = Environment.GetFolderPath(Environment.SpecialFolder.CommonPictures) + "\\Sample_Pictures";
Or use
string publicDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory);
var directory = new DirectoryInfo(publicDesktopPath);
string path1 = directory.Parent.FullName + "\\Pictures\\Sample_Pictures";
and fix your conditional
if (!Directory.Exists(path1)) {
to
if (Directory.Exists(path1)) {
so that you don't try operations on an non-existent directory.
To get it to cycle through your pictures, you could use a System.Timers.Timer:
In your Form1 class
private static Timer timer;
Declare the timer in your constructor:
timer = new System.Timers.Timer(5000); // change interval in milliseconds
timer.Elapsed += OnTimedEvent;
timer.Enabled = true;
Create the OnTimedEvent method in your Form1 class:
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
// Do what you want every time the timer elapses that interval
nextImage();
}
I'm using MonoDevelop tool.
I want to make program like below with GTK#;
1. User drags file to program's listView, tableView or whatever
2. Dragged file's list is printed on the program's window
But I'm almost new to GTK# and find the way how to drag and drop,
so I searched information about it and found this link like below.
http://developer.gnome.org/gtkmm-tutorial/3.0/sec-dnd-example.html.en
This is the code I tried to make program link suggested
(This link explains drag and drop in C++ and I had to make it the way like C#)
using Gtk;
using System;
using System.Collections;
using System.Collections.Generic;
namespace DragAndDrop
{
public class SharpApp: Window
{
Button btnDrag;
Label lblDrop;
HBox hbox;
public SharpApp (): base("Title")
{
btnDrag = new Button("Drag Here");
lblDrop = new Label("Drop Here");
hbox = new HBox();
SetDefaultSize(250,200);
SetPosition (Gtk.WindowPosition.Center);
DeleteEvent += (o, args) => Application.Quit ();
// Targets
List<TargetEntry> list
= new List<TargetEntry> ();
list.Add (new TargetEntry
("STRING", TargetFlags.Widget, 0));
list.Add (new TargetEntry
("text/plain", TargetFlags.Widget, 0));
// Drag site -----
// Make btnDrag a DnD drag source:
TargetEntry[] entries = list.ToArray();
TargetEntry[] se = new TargetEntry[] {entries[0]};
Drag.SourceSet (btnDrag, Gdk.ModifierType.ModifierMask,
se, Gdk.DragAction.Copy);
// Connect signals
btnDrag.DragDataGet += delegate
(object o, DragDataGetArgs args) {
Console.WriteLine ("Test");
OnDragDataGet(args.Context,
args.SelectionData,
args.Info,
args.Time);
};
hbox.PackStart (btnDrag);
// Drop site -----
// Make lblDrop a DnD drop destination:
TargetEntry[] de = new TargetEntry[] {entries[1]};
Drag.DestSet (lblDrop, DestDefaults.Drop,
de, Gdk.DragAction.Copy);
// Connect signals
lblDrop.DragDataReceived += delegate
(object o, DragDataReceivedArgs args) {
Console.WriteLine ("Test");
OnDragDataReceived(args.Context,
args.X,
args.Y,
args.SelectionData,
args.Info,
args.Time);
};
// hbox
hbox.PackStart (lblDrop);
Add (hbox);
ShowAll ();
}
// event handlers
protected override void OnDragDataGet
(Gdk.DragContext context, SelectionData sdata,
uint info, uint time)
{
Console.WriteLine ("OnDragDataGet");
string tmp = "I'm data!";
byte[] b = new byte[tmp.Length];
for (int i=0; i<tmp.Length; ++i)
b[i] = (byte)tmp[i];
sdata.Set(sdata.Target, 8, b, tmp.Length);
}
protected override void OnDragDataReceived
(Gdk.DragContext context, int x, int y,
SelectionData sdata, uint info, uint time)
{
Console.WriteLine ("OnDragDataReceived");
int length = sdata.Length;
if ((length>=0) && (sdata.Format==8))
{
Console.WriteLine ("Received \"{0}\" in label",
sdata.Data.ToString());
}
Drag.Finish (context, false, false, time);
}
// main
public static void Main (string[] args)
{
Application.Init ();
new SharpApp();
Application.Run ();
}
}
}
but the result tells me I might be wrong.. I thought the button will be moved
when I drag it, but the button didn't move at all. Is there anyone able to fix my problem?
The following example prints into a label (called lblPath) the full path of the files dropped over it:
public partial class MainWindow: Gtk.Window
{
/** Add a label to your project called lblPath **/
public MainWindow () : base (Gtk.WindowType.Toplevel)
{
Build ();
//media type we'll accept
Gtk.TargetEntry [ ] target_table =
new TargetEntry [ ] {
new TargetEntry ("text/uri-list", 0, 0),
new TargetEntry ("application/x-monkey", 0, 1),
};
Gtk.Drag.DestSet (lblPath, DestDefaults.All, target_table, Gdk.DragAction.Copy);
lblPath.DragDataReceived += new Gtk.DragDataReceivedHandler(OnLabelDragDataReceived);
}
void OnLabelDragDataReceived (object sender, Gtk.DragDataReceivedArgs args)
{
if (args.SelectionData.Length > 0) {
byte[] data = args.SelectionData.Data;
string files = System.Text.Encoding.UTF8.GetString (data);
string file;
string[] fileArray = files.Split ('\n');
for (int i = 0; i < fileArray.Length - 1; i++) { //the last element is empty
file = fileArray [i].Replace ("\r", "");
if (file.StartsWith("file://"))
file = file.Substring(7); //for Windows should be 8
lblPath.Text += file + "\n";
}
}
}
}
Self answer
I found tutorials about drag and drop here:
https://github.com/mono/gtk-sharp/blob/master/sample/TestDnd.cs
http://my.safaribooksonline.com/book/programming/mono/0596007922/gtksharp/monoadn-chp-4-sect-8
and I made a tutorial of drag and drop myself.
This is drop file tutorial.
using System;
using System.Collections;
using System.Collections.Generic;
// 1. Drop file tutorial
namespace DropFile
{
public class App: Gtk.Window
{
Gtk.Label lbldrop;
public App (): base ("Drop file")
{
this.SetDefaultSize (250, 200);
this.SetPosition (Gtk.WindowPosition.Center);
this.DeleteEvent += OnTerminated;
this.lbldrop = new Gtk.Label ("Drop here!");
Gtk.Drag.DestSet (this.lbldrop, 0, null, 0);
this.lbldrop.DragDrop
+= new Gtk.DragDropHandler
(OnLabelDragDrop);
this.lbldrop.DragDataReceived
+= new Gtk.DragDataReceivedHandler
(OnLabelDragDataReceived);
Gtk.VBox vbox = new Gtk.VBox ();
vbox.PackStart (this.lbldrop, true, true, 0);
this.Add (vbox);
this.ShowAll ();
}
void OnLabelDragDrop (object sender, Gtk.DragDropArgs args)
{
Gtk.Drag.GetData
((Gtk.Widget)sender, args.Context,
args.Context.Targets[0], args.Time);
}
void OnLabelDragDataReceived
(object sender, Gtk.DragDataReceivedArgs args)
{
if (args.SelectionData.Length > 0
&& args.SelectionData.Format == 8) {
byte[] data = args.SelectionData.Data;
string encoded = System.Text.Encoding.UTF8.GetString (data);
// I don't know what last object is,
// but I tested and noticed that it is not
// a path
List<string> paths
= new List<string> (encoded.Split ('\r', '\n'));
paths.RemoveAll (string.IsNullOrEmpty);
paths.RemoveAt (paths.Count-1);
for (int i=0; i<paths.Count; ++i)
{
Console.WriteLine ("Path {0}: {1}", i, paths[i]);
}
}
}
bool Test (string str)
{
return true;
}
void OnTerminated (object sender, EventArgs args)
{
Gtk.Application.Quit ();
}
public static void Main (string[] args)
{
Gtk.Application.Init ();
new App ();
Gtk.Application.Run ();
}
}
}
And this is drag button tutorial.
using System;
// 2. Drag button tutorial
namespace DragButton
{
public class App: Gtk.Window
{
enum StatusType
{
Checked,
NotChecked
}
Gtk.Button btnDrag;
Gtk.Label lblDrop;
bool isChecked;
Gtk.Statusbar sBar;
public App (): base("Drag And Drop Complete")
{
this.SetDefaultSize (250, 200);
this.SetPosition (Gtk.WindowPosition.Center);
this.DeleteEvent += OnTerminated;
this.isChecked = false;
this.sBar = new Gtk.Statusbar ();
sBar.Push ((uint)StatusType.NotChecked, "Not checked");
this.btnDrag = new Gtk.Button ("Drag here");
Gtk.Drag.SourceSet
(this.btnDrag,
Gdk.ModifierType.Button1Mask
| Gdk.ModifierType.Button3Mask,
null,
Gdk.DragAction.Copy | Gdk.DragAction.Move);
this.btnDrag.DragDataGet
+= new Gtk.DragDataGetHandler
(HandleSourceDragDataGet);
this.btnDrag.DragDataDelete
+= new Gtk.DragDataDeleteHandler
(HandleSourceDragDataDelete);
// set drop label as destination
this.lblDrop = new Gtk.Label ("Drop here");
Gtk.Drag.DestSet (this.lblDrop, 0, null, 0);
this.lblDrop.DragMotion
+= new Gtk.DragMotionHandler
(HandleTargetDragMotion);
this.lblDrop.DragDrop
+= new Gtk.DragDropHandler
(HandleTargetDragDrop);
this.lblDrop.DragDataReceived
+= new Gtk.DragDataReceivedHandler
(this.HandleTargetDragDataReceived);
this.lblDrop.DragDrop
+= new Gtk.DragDropHandler
(this.HandleStatBarDragDrop);
Gtk.MenuBar bar = new Gtk.MenuBar ();
Gtk.MenuItem item = new Gtk.MenuItem ("File");
Gtk.Menu menu = new Gtk.Menu ();
item.Submenu = menu;
bar.Append (item);
// accel key
Gtk.AccelGroup ag = new Gtk.AccelGroup ();
this.AddAccelGroup (ag);
item = new Gtk.MenuItem ("Quit");
item.Activated += OnTerminated;
item.AddAccelerator
("activate", ag, new Gtk.AccelKey
(Gdk.Key.Q, Gdk.ModifierType.ControlMask,
Gtk.AccelFlags.Visible));
menu.Append (item);
Gtk.VBox vbox = new Gtk.VBox();
vbox.PackStart (bar, false, false, 0);
Gtk.HBox hbox = new Gtk.HBox ();
hbox.PackStart (this.btnDrag, true, true, 0);
hbox.PackStart (this.lblDrop, true, true, 0);
vbox.PackStart (hbox, true, true, 0);
vbox.PackStart (sBar, false, false, 0);
this.Add (vbox);
this.ShowAll ();
}
void OnTerminated(object sender, EventArgs args)
{
Gtk.Application.Quit ();
}
void HandleSourceDragDataGet
(object sender, Gtk.DragDataGetArgs args)
{
Console.WriteLine ("Source Drag Data Get");
args.SelectionData.Text = "I'm data!";
}
void HandleSourceDragDataDelete
(object sender, Gtk.DragDataDeleteArgs args)
{
Console.WriteLine ("Source Drag Data Delete");
Console.WriteLine ("Delete the data!");
}
void HandleTargetDragMotion
(object sender, Gtk.DragMotionArgs args)
{
Gdk.Drag.Status (args.Context,
args.Context.SuggestedAction,
args.Time);
args.RetVal = true;
}
void HandleTargetDragDrop
(object sender, Gtk.DragDropArgs args)
{
Console.WriteLine ("drop");
if (args.Context.Targets.Length != 0) {
Gtk.Drag.GetData
((Gtk.Widget)sender, args.Context,
args.Context.Targets[0], args.Time);
args.RetVal = true;
}
args.RetVal = false;
}
void HandleTargetDragDataReceived
(object sender, Gtk.DragDataReceivedArgs args)
{
Console.WriteLine ("received");
if (args.SelectionData.Length >= 0
&& args.SelectionData.Format == 8)
{
Console.WriteLine ("Hi!");
Gtk.Drag.Finish (args.Context, true, false, args.Time);
}
Gtk.Drag.Finish (args.Context, false, false, args.Time);
}
void HandleStatBarDragDrop
(object sender, Gtk.DragDropArgs args)
{
isChecked = !isChecked;
if (isChecked)
sBar.Push ((uint)StatusType.Checked, "Checked");
else
sBar.Pop ((uint)StatusType.Checked);
}
public static void Main (string[] args)
{
Gtk.Application.Init ();
new App ();
Gtk.Application.Run ();
}
}
}