Я использую сообщество Visual Studio 2017. В форме пользовательского интерфейса проекта System.Windows.Forms.WebBrowser используется для предварительного просмотра PDF-документа. Мне было поручено добавить возможность предварительного просмотра указанного документа также с помощью Chrome, а не только Explorer.
Для этого я установил пакет NuGet CefSharp.WinForms и использовал класс ChromiumWebBrowser. Проблема заключается в том, что в исходном виде поле PDFPreview объявляется как веб-браузер и на него ссылаются около 20 раз. Кроме того, классы WebBrowser и ChromiumWebBrowser очень похожи, но не каждый метод или поле называются одним и тем же именем / способом или имеют соответствующий аналог.
Я думал, что решение может включать использование обобщений или интерфейсов, но сейчас ятолько что создал новый класс Browser, который создает экземпляр и использует либо поле WebBrowser, либо ChromiumWebBrowser в соответствии с логическим значением, переданным в конструкторе, и для каждого метода, который мне нужно использовать, из перечисленных выше классов я переопределил его внутри Browser, используя их исходные методы, в соответствии с тем, какой объект IЯ работаю над тем, как если бы я переопределял методы обоих классов (к сожалению, я знаю, что не могу заставить браузер расширять несколько классов, и я не могу создать интерфейс, реализованный WebBrowser и ChromiumWebBrowser, так как те не могут менялись).
Извините, если я объясняю это очень грязно или неправильно использую термины, я все еще учусь кодировать, но, надеюсь, сам код прояснит ситуацию.
Это ориginal WebBrowser field и все используемые методы / поля:
WebBrowser PDFPreview = null;
PDFPreview.IsDisposed
PDFPreview.Url == new UriBuilder(documentFilePath).Uri
PDFPreview = new WebBrowser();
scannerOperationContainer.Panel2.Controls.Add(PDFPreview);
PDFPreview.Dock = DockStyle.Fill;
PDFPreview.Navigate(documentFilePath);
PDFPreview.Document.Body.Style = "zoom:50%";
PDFPreview.Hide();
PDFPreview.Stop();
PDFPreview.Dispose();
Вот класс Browser, который я создал как «cointaner» для WebBrowser и ChromiumWebBrowser:
class Browser
{
public bool useChrome;
public WebBrowser explorerBrowser = null;
public ChromiumWebBrowser chromeBrowser = null;
public bool isDisposed
{
get{return IsDisposed();}
}
public Uri url
{
get {return Url();}
}
public DockStyle dock
{
get { return Dock(); }
set
{
if (useChrome)
{ chromeBrowser.Dock = value; }
else
{ explorerBrowser.Dock = value; }
}
}
public HtmlDocument document
{
get { return Document(); }
}
public static explicit operator WebBrowser(Browser b) => b.explorerBrowser;
public static explicit operator ChromiumWebBrowser(Browser b) => b.chromeBrowser;
public Browser(bool useChrome)
{
if (useChrome)
{
//chromeBrowser = new ChromiumWebBrowser("https://www.google.com/");
//Since Load doesn't seem to work I create the chromeBrowser directly when I would need to load
//to a new URL, it's bad but for how it's written the code in the parent form it's making it work
this.useChrome = useChrome;
}
else
{
explorerBrowser = new WebBrowser();
this.useChrome = useChrome;
}
}
public void Navigate(string urlString)
{
if (useChrome)
{
chromeBrowser = new ChromiumWebBrowser(urlString);
}
else
{
explorerBrowser.Navigate(urlString);
}
}
public void Hide()
{
if (useChrome)
{
chromeBrowser.Hide();
}
else
{
explorerBrowser.Hide();
}
}
public void Stop()
{
if (useChrome)
{
chromeBrowser.Stop();
}
else
{
explorerBrowser.Stop();
}
}
public void Dispose()
{
if (useChrome)
{
chromeBrowser.Dispose();
}
else
{
explorerBrowser.Dispose();
}
}
//***
//GETTERS AND SETTERS
//***
public bool IsDisposed()
{
if (useChrome)
{
return chromeBrowser.IsDisposed;
}
else
{
return explorerBrowser.IsDisposed;
}
}
public Uri Url()
{
if (useChrome)
{
return new UriBuilder(chromeBrowser.Address).Uri;
}
else
{
return explorerBrowser.Url;
}
}
public DockStyle Dock()
{
if (useChrome)
{
return chromeBrowser.Dock;
}
else
{
return explorerBrowser.Dock;
}
}
public HtmlDocument Document()
{
if (useChrome)
{
return null; //Will never get here, see form1
}
else
{
return explorerBrowser.Document;
}
}
}
Я хотел изменитьсформировать исходный код как можно меньше, но мне пришлось изменить несколько битов, чтобы он работал:
Мне нужно вызвать Navigate () сразу после его создания, а также есть проверка if, если необходимо привести к WebBrowserили ChromiumWebBrowser по адресу scannerOperationContainer.Panel2.Controls.Add (PDFPreview).
Хотя основное и, я надеюсь, единственное изменение, которое я хотел бы иметь, было бы следующим:
Browser PDFPreview = null;
PDFPreview = new Browser(ConfigurationDocument.GetBooleanProperty("EnableChromePdfViewer"));
Этот код работает какна данный момент, но я подозреваю, что это очень уродливо и плохо продумано.
Мне было интересно, должен ли быть более умный / более элегантный / правильный способ решения этой проблемы. Я подумал, что, возможно, Browser может быть универсальным классом и каким-то образом передать из config.xml правильный тип класса? Хотя я не знаю, как это написать. Или, может быть, создать два класса, которые расширяют WebBrowser и ChromiumWebBrowser, а затем имеют интерфейс, реализованный обоими? Или правильная форма в другом направлении полностью?