Contents
Introduction
CP Article Editor allows you to create/edit articles for CodeProject without internet connection requirement. You can also login to backup (both your articles and related images) and edit your published articles using this program. It is very small in size and portable.
(Click on the images to view the full sized images in a new window.)
Background
Let's learn or remember something that we will need to understand the source code.
Asynchronous Operations
Asynchronous operations work separately and they have their own threads. If we need to execute codes after they finished their works we can't write these codes below the method it starts them. Because they don't block the method they are called from. Another thing that we must know is that a thread can't access an object created on a different thread...
Generally we are using event
s for operations that must be done after an asynchronous method finished its mission or an event occured. But I used a different way for this purpose and I think it is relatively simpler to understand.
Suppose that we have a thread and we want to call a function after our thread finished its work:
class Example
{
private Action after_MyThreadFinished;
private Form invoker;
public Example(Action after_MyThreadFinished, Form invoker)
{
this.after_MyThreadFinished = after_MyThreadFinished;
this.invoker = invoker;
}
public void doSomeWorksAsynchronously()
{
new Thread(() =>
{
Thread.Sleep(2000);
if (after_MyThreadFinished != null)
invoker.Invoke(after_MyThreadFinished);
}).Start();
}
}
A thread can't make any change directly on a visual object owned by an UI thread. And we can't be sure about that the user will not use codes in our new thread to access visual controls created by the UI thread. This is why we need an invoker object.
Usage:
private void button1_Click(object sender, EventArgs e)
{
Action myAction = new Action(() =>
{
MessageBox.Show("Finished.");
this.Text = "I can access all the objects without any trouble." +
"Because I will be invoked by the thread of this form.";
});
Example example = new Example(myAction, this);
example.doSomeWorksAsynchronously();
}
CKEditor
CKEditor is an open source WYSIWYG editor that can be integrated with web pages easily. I used it inside a webbrowser in this project.
I customized the toolbar and removed unneccessary plugins to reduce total size. Additionally I modified image.js plugin to make using relative urls possible:
if(s.$.src.substring(0,7)=='http://' || s.$.src.substring(0,8)=='https://')
B.preview.setAttribute('src',s.$.src);
else
{
var kok=(document.getElementsByTagName('base')[0]==null?'**biseyyapma**':
document.getElementsByTagName('base')[0].getAttribute('href'));
var verilenAdres=C;
var tamAdres=kok+C;
B.preview.setAttribute('src',tamAdres);
}
kok=(document.getElementsByTagName('base')[0]==null?'**biseyyapma**':
document.getElementsByTagName('base')[0].getAttribute('href')),
D=(D.indexOf(kok)>-1?D.replace(kok,''):D),
var verilenAdres=D.getValue();
if(verilenAdres.substring(0,7)=='http://' || verilenAdres.substring(0,8)=='https://')
{
C.data('cke-saved-src',D.getValue());
C.setAttribute('src',D.getValue());
}
else
{
var kok=(document.getElementsByTagName('base')[0]==null?'**biseyyapma**':
document.getElementsByTagName('base')[0].getAttribute('href'));
var tamAdres=(verilenAdres.indexOf(kok)<0?kok+verilenAdres:verilenAdres)
C.data('cke-saved-src',tamAdres);
C.setAttribute('src',tamAdres);
}
Used configurations:
fullPage : false // We are not editing a full page
language : 'en' // I removed all other language files to decrease the size.
resize_enabled : false // Prevent resizing the editor.
uiColor : '#CCC' // Grey
tabSpaces : 4 // Each tab character is replaced with 4 spaces.
contentsCss : 'CodeProject.css' // CSS stylesheet file of codeproject template.
removePlugins : 'contextmenu,liststyle,tabletools' // For hiding ckeditor's context menu
docType : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"' +
'"http://www.w3.org/TR/html4/loose.dtd">' // Document type of CodeProject.
Accessing And Modifying Elements Of An Html Document
I used a WebBrowser
to login and extract required data. Here are some important methods.
Reading an attribute of an element:
webBrowser.Document.GetElementById("IdOfTheElement").GetAttribute("attribute");
Changing an attribute of an element:
webBrowser.Document.GetElementById("IdOfTheElement").SetAttribute("attribute", "new value");
Calling a javascript function that is inside a WebBrowser
object from c#:
object[] args = new object[] { ... };
object returned = webBrowser.Document.InvokeScript("functionName", args);
Downloading Files From A Website
There is a very simple method for downloading files from a website. I used that method to download images related with an article.
WebClient webClient = new WebClient();
webClient.DownloadFile("http://www.site.com/file.extension", "C:\file.extension");
Disabling Click Sound In A WebBrowser
There isn't a property to disable the click sound in a web browser. But there is a way to do that using CoInternetSetFeatureEnabled API:
static class ClickSoundDisabler
{
private const int FEATURE_DISABLE_NAVIGATION_SOUNDS = 21;
private const int SET_FEATURE_ON_PROCESS = 0x00000002;
[DllImport("urlmon.dll")]
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
private static extern int CoInternetSetFeatureEnabled(int FeatureEntry,
[MarshalAs(UnmanagedType.U4)] int dwFlags, bool fEnable);
public static void disableClickSound()
{
try
{
CoInternetSetFeatureEnabled(FEATURE_DISABLE_NAVIGATION_SOUNDS,
SET_FEATURE_ON_PROCESS, true);
}
catch{}
}
}
Screenshots
This is the main window of the program:
If you want to backup your published articles to edit or just to save them you can enter your email and password to login. Your articles will be listed after you logged in:
When you choosed one of them, some informations about it will appear:
You can write a new article or edit an existing one even if you are not connected to the internet:
Transferring Articles To CodeProject's Online Editor
- Select "Copy source to the clipboard" from the right-click menu.
(Note that all absolute image paths are converted to relative paths. So you don't have to change anything on copied html codes.) - Open codeproject online editor.
- Click on "HTML" button to switch to HTML mode:
- Paste your contents. (CTRL+V)
- Click again on "HTML" button to switch back to design mode...
Points Of Interest
Let me list things that we can learn while examining the source codes:
- WYSIWYG html editor in .NET.
- How to use ckeditor in .NET?
- How to call a javascript function inside a
WebBrowser
from a c# function? - Automated login and data extraction.
- Using
Action
s instead of Event
s for asynchronous functions in c#. - Disabling click sound in a WebBrowser control.
- Downloading files from internet in c#.
History
- 15 December 2013 (v1.0.4)
- Article listing and login methods were fixed after some changes on CodeProject.
- 4 April 2013 (v1.0.3)
- Logins have failed recently because of some updates on CodeProject. Fixed.
- Random strings were appended to the urls to force the WebBrowser to reload cached pages.
- 17 October 2012 (v1.0.2)
- New codes to remember last browsed path
- A small change in
extractMemberId()
function - "Open with" support
(You can use "Open with" menu or drag a cpa file and drop it on icon of the program.) - A unique file header to validate CPA (CodeProject Article) files
(Because of that old cpa files cannot be opened with this version.)
- 5 October 2012 (v1.0.1)
- "ctl00" became "ctl01" on the login page of codeproject. So a new function (
getElementId()
) was written to get IDs of elements using Regex class and this function was used instead of all old constants that will be able to be changed by codeproject in future.
- 1 October 2012 (v1.0.0)